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#include <ostream>
19
20using namespace std;
21
24const int PRECISION_DIGITS = 4;
25
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 qCInfo(ENGAUGE_LOG) << "Transformation::calculateTransformFromLinearCartesianPoints";
67
68 QTransform from, to;
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
85 QPointF posGraphCartesian = posGraphIn;
86
87 if (modelCoords.coordsType() == COORDS_TYPE_POLAR) {
88
89 // Input coordinates are polar so convert them
90 double angleRadians = 0; // Initialized to prevent compiler warning
91 switch (modelCoords.coordUnitsTheta())
92 {
97 angleRadians = posGraphIn.x () * M_PI / 180.0;
98 break;
99
101 angleRadians = posGraphIn.x () * M_PI / 200.0;
102 break;
103
105 angleRadians = posGraphIn.x ();
106 break;
107
109 angleRadians = posGraphIn.x () * 2.0 * M_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
128 QPointF posGraphCartesianOrPolar = posGraphIn;
129
130 if (modelCoords.coordsType() == COORDS_TYPE_POLAR) {
131
132 // Output coordinates are to be polar so convert them
133 double angleRadians = qAtan2 (posGraphIn.y (),
134 posGraphIn.x ());
135 switch (modelCoords.coordUnitsTheta())
136 {
141 posGraphCartesianOrPolar.setX (angleRadians * 180.0 / M_PI);
142 break;
143
145 posGraphCartesianOrPolar.setX (angleRadians * 200.0 / M_PI);
146 break;
147
149 posGraphCartesianOrPolar.setX (angleRadians);
150 break;
151
153 posGraphCartesianOrPolar.setX (angleRadians / (2.0 * M_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
164 return posGraphCartesianOrPolar;
165}
166
167void Transformation::coordTextForStatusBar (QPointF cursorScreen,
168 QString &coordsScreen,
169 QString &coordsGraph,
170 QString &resolutionsGraph,
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
177 QString needMoreText = (usingScaleBar ?
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
198 QPointF cursorScreenDelta (cursorScreen.x () + X_DELTA_PIXELS,
199 cursorScreen.y () + Y_DELTA_PIXELS);
200
201 // Convert to graph coordinates
202 QPointF pointGraph, pointGraphDelta;
203 transformScreenToRawGraph (cursorScreen,
204 pointGraph);
205 transformScreenToRawGraph (cursorScreenDelta,
206 pointGraphDelta);
207
208 // Compute graph resolutions at cursor
209 double resolutionXGraph = qAbs ((pointGraphDelta.x () - pointGraph.x ()) / X_DELTA_PIXELS);
210 double resolutionYGraph = qAbs ((pointGraphDelta.y () - pointGraph.y ()) / Y_DELTA_PIXELS);
211
212 // Formatting for date/time and degrees/minutes/seconds is only done on coordinates, and not on resolution
213 FormatCoordsUnits format;
214 QString xThetaFormatted, yRadiusFormatted;
215 format.unformattedToFormatted (pointGraph.x(),
216 pointGraph.y(),
217 m_modelCoords,
218 m_modelGeneral,
219 m_modelMainWindow,
220 xThetaFormatted,
221 yRadiusFormatted,
222 *this);
223
224 coordsGraph = QString ("(%1, %2)")
225 .arg (xThetaFormatted)
226 .arg (yRadiusFormatted);
227
228 resolutionsGraph = QString ("(%1, %2)")
229 .arg (resolutionXGraph, UNCONSTRAINED_FIELD_WIDTH, FORMAT, PRECISION_DIGITS)
230 .arg (resolutionYGraph, UNCONSTRAINED_FIELD_WIDTH, FORMAT, PRECISION_DIGITS);
231
232 } else {
233
234 coordsGraph = QString ("<font color=\"red\">%1</font>")
235 .arg (needMoreText);
236 resolutionsGraph = coordsGraph;
237
238 }
239 }
240}
241
243{
244 // Initialize assuming points (0,0) (1,0) (0,1)
245 m_transformIsDefined = true;
246
247 QTransform ident;
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
277ostream &operator<<(ostream &strOuter,
278 const Transformation &transformation)
279{
280 QString text;
281 QTextStream strInner (&text);
282 transformation.printStream ("", strInner);
283
284 strOuter << text.toLatin1().data ();
285
286 return strOuter;
287}
288
289QDebug operator<<(QDebug debug,
290 const Transformation &transformation)
291{
292 QString text;
293 QTextStream strInner (&text);
294 transformation.printStream ("", strInner);
295
296 debug << text;
297
298 return debug;
299}
300
301void Transformation::printStream (QString indentation,
302 QTextStream &str) const
303{
304 str << "Transformation\n";
305
306 indentation += INDENTATION_DELTA;
307
308 if (m_transformIsDefined) {
309
310 str << indentation << "affine=" << (m_transform.isAffine() ? "yes" : "no") << " matrix=("
311 << m_transform.m11() << ", " << m_transform.m12() << ", " << m_transform.m13() << ", "
312 << m_transform.m21() << ", " << m_transform.m22() << ", " << m_transform.m23() << ", "
313 << m_transform.m31() << ", " << m_transform.m32() << ", " << m_transform.m33() << ")";
314
315 } else {
316
317 str << indentation << "undefined";
318
319 }
320}
321
323{
324 qCInfo(ENGAUGE_LOG) << "Transformation::resetOnLoad";
325
326 m_transformIsDefined = false;
327}
328
329double Transformation::roundOffSmallValues (double value, double range)
330{
331 if (qAbs (value) < range / qPow (10.0, PRECISION_DIGITS)) {
332 value = 0.0;
333 }
334
335 return value;
336}
337
338void Transformation::setModelCoords (const DocumentModelCoords &modelCoords,
339 const DocumentModelGeneral &modelGeneral,
340 const MainWindowModel &modelMainWindow)
341{
342 m_modelCoords = modelCoords;
343 m_modelGeneral = modelGeneral;
344 m_modelMainWindow = modelMainWindow;
345}
346
348{
349 return m_transformIsDefined;
350}
351
352void Transformation::transformLinearCartesianGraphToRawGraph (const QPointF &pointLinearCartesianGraph,
353 QPointF &pointRawGraph) const
354{
355 // WARNING - the code in this method must mirror the code in transformRawGraphToLinearCartesianGraph. In
356 // other words, making a change here without a corresponding change there will produce a bug
357
358 pointRawGraph = pointLinearCartesianGraph;
359
360 // Apply polar coordinates if appropriate
361 if (m_modelCoords.coordsType() == COORDS_TYPE_POLAR) {
362 pointRawGraph = cartesianOrPolarFromCartesian (m_modelCoords,
363 pointRawGraph);
364 }
365
366 // Apply linear offset to radius if appropriate
367 if ((m_modelCoords.coordsType() == COORDS_TYPE_POLAR) &&
368 (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LINEAR)) {
369 pointRawGraph.setY (pointRawGraph.y() + m_modelCoords.originRadius());
370 }
371
372 // Apply log scaling if appropriate
373 if (m_modelCoords.coordScaleXTheta() == COORD_SCALE_LOG) {
374 pointRawGraph.setX (qExp (pointRawGraph.x()));
375 }
376
377 if (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LOG) {
378 double offset;
379 if (m_modelCoords.coordsType() == COORDS_TYPE_CARTESIAN) {
380 // Cartesian
381 offset = ZERO_OFFSET_AFTER_LOG;
382 } else {
383 // Polar radius
384 offset = m_modelCoords.originRadius();
385 }
386
387 pointRawGraph.setY (qExp (pointRawGraph.y() + qLn (offset)));
388 }
389}
390
392 QPointF &coordScreen) const
393{
394 ENGAUGE_ASSERT (m_transformIsDefined);
395
396 coordScreen = m_transform.inverted ().transposed ().map (coordGraph);
397}
398
400{
401 return m_transform;
402}
403
405 QPointF &pointLinearCartesian) const
406{
407 // WARNING - the code in this method must mirror the code in transformLinearCartesianGraphToRawGraph. In
408 // other words, making a change here without a corresponding change there will produce a bug
409
410 double x = pointRaw.x();
411 double y = pointRaw.y();
412
413 // Apply linear offset to radius if appropriate
414 if ((m_modelCoords.coordsType() == COORDS_TYPE_POLAR) &&
415 (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LINEAR)) {
416 y -= m_modelCoords.originRadius();
417 }
418
419 // Apply log scaling if appropriate
420 if (m_modelCoords.coordScaleXTheta() == COORD_SCALE_LOG) {
421 x = logToLinearCartesian (x);
422 }
423
424 if (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LOG) {
425 if (m_modelCoords.coordsType() == COORDS_TYPE_POLAR) {
426 y = logToLinearRadius (y,
427 m_modelCoords.originRadius());
428 } else {
429 y = logToLinearRadius (y,
431 }
432 }
433
434 // Apply polar coordinates if appropriate. Note range coordinate has just been transformed if it has log scaling
435 if (m_modelCoords.coordsType() == COORDS_TYPE_POLAR) {
436 QPointF pointCart = cartesianFromCartesianOrPolar (m_modelCoords,
437 QPointF (x, y));
438 x = pointCart.x();
439 y = pointCart.y();
440 }
441
442 pointLinearCartesian.setX (x);
443 pointLinearCartesian.setY (y);
444}
445
446void Transformation::transformRawGraphToScreen (const QPointF &pointRaw,
447 QPointF &pointScreen) const
448{
449 QPointF pointLinearCartesianGraph;
450
452 pointLinearCartesianGraph);
453 transformLinearCartesianGraphToScreen (pointLinearCartesianGraph,
454 pointScreen);
455}
456
458 QPointF &coordGraph) const
459{
460 ENGAUGE_ASSERT (m_transformIsDefined);
461
462 coordGraph = m_transform.transposed ().map (coordScreen);
463}
464
465void Transformation::transformScreenToRawGraph (const QPointF &coordScreen,
466 QPointF &coordGraph) const
467{
468 QPointF pointLinearCartesianGraph;
470 pointLinearCartesianGraph);
471 transformLinearCartesianGraphToRawGraph (pointLinearCartesianGraph,
472 coordGraph);
473}
474
475void Transformation::update (bool fileIsLoaded,
476 const CmdMediator &cmdMediator,
478{
479 qCDebug(ENGAUGE_LOG) << "Transformation::update";
480
481 if (!fileIsLoaded) {
482
483 m_transformIsDefined = false;
484
485 } else {
486
487 setModelCoords (cmdMediator.document().modelCoords(),
488 cmdMediator.document().modelGeneral(),
490
491 CallbackUpdateTransform ftor (m_modelCoords,
492 cmdMediator.document().documentAxesPointsRequired());
493
494 Functor2wRet<const QString &, const Point&, CallbackSearchReturn> ftorWithCallback = functor_ret (ftor,
496 cmdMediator.iterateThroughCurvePointsAxes (ftorWithCallback);
497
498 if (ftor.transformIsDefined ()) {
499
500 updateTransformFromMatrices (ftor.matrixScreen(),
501 ftor.matrixGraph());
502
503 } else {
504
505 m_transformIsDefined = false;
506
507 }
508 }
509}
510
511void Transformation::updateTransformFromMatrices (const QTransform &matrixScreen,
512 const QTransform &matrixGraph)
513{
514 // LOG4CPP_INFO_S is below
515
516 m_transformIsDefined = true;
517
518 // Extract points from 3x3 matrices
519 QPointF pointGraphRaw0 (matrixGraph.m11(),
520 matrixGraph.m21());
521 QPointF pointGraphRaw1 (matrixGraph.m12(),
522 matrixGraph.m22());
523 QPointF pointGraphRaw2 (matrixGraph.m13(),
524 matrixGraph.m23());
525
526 QPointF pointGraphLinearCart0, pointGraphLinearCart1, pointGraphLinearCart2;
528 pointGraphLinearCart0);
530 pointGraphLinearCart1);
532 pointGraphLinearCart2);
533
534 // Calculate the transform
535 m_transform = calculateTransformFromLinearCartesianPoints (QPointF (matrixScreen.m11(), matrixScreen.m21()),
536 QPointF (matrixScreen.m12(), matrixScreen.m22()),
537 QPointF (matrixScreen.m13(), matrixScreen.m23()),
538 QPointF (pointGraphLinearCart0.x(), pointGraphLinearCart0.y()),
539 QPointF (pointGraphLinearCart1.x(), pointGraphLinearCart1.y()),
540 QPointF (pointGraphLinearCart2.x(), pointGraphLinearCart2.y()));
541
542 // Logging
543 QTransform matrixGraphLinear (pointGraphLinearCart0.x(),
544 pointGraphLinearCart1.x(),
545 pointGraphLinearCart2.x(),
546 pointGraphLinearCart0.y(),
547 pointGraphLinearCart1.y(),
548 pointGraphLinearCart2.y());
549
550 QPointF pointScreenRoundTrip0, pointScreenRoundTrip1, pointScreenRoundTrip2;
551 transformRawGraphToScreen (pointGraphRaw0,
552 pointScreenRoundTrip0);
553 transformRawGraphToScreen (pointGraphRaw1,
554 pointScreenRoundTrip1);
555 transformRawGraphToScreen (pointGraphRaw2,
556 pointScreenRoundTrip2);
557
558 QPointF pointScreen0 (matrixScreen.m11(),
559 matrixScreen.m21());
560 QPointF pointScreen1 (matrixScreen.m12(),
561 matrixScreen.m22());
562 QPointF pointScreen2 (matrixScreen.m13(),
563 matrixScreen.m23());
564
565 qCInfo(ENGAUGE_LOG) << "Transformation::updateTransformFromMatrices"
566 << " matrixScreen=\n" << QTransformToString (matrixScreen).toLatin1().data () << " "
567 << " matrixGraphRaw=\n" << QTransformToString (matrixGraph).toLatin1().data() << " "
568 << " matrixGraphLinear=\n" << QTransformToString (matrixGraphLinear).toLatin1().data() << "\n"
569 << " originalScreen0=" << QPointFToString (pointScreen0).toLatin1().data() << "\n"
570 << " originalScreen1=" << QPointFToString (pointScreen1).toLatin1().data() << "\n"
571 << " originalScreen2=" << QPointFToString (pointScreen2).toLatin1().data() << "\n"
572 << " roundTripScreen0=" << QPointFToString (pointScreenRoundTrip0).toLatin1().data() << "\n"
573 << " roundTripScreen1=" << QPointFToString (pointScreenRoundTrip1).toLatin1().data() << "\n"
574 << " roundTripScreen2=" << QPointFToString (pointScreenRoundTrip2).toLatin1().data() << "\n";
575}
@ 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
#define ENGAUGE_ASSERT(cond)
Drop in replacement for Q_ASSERT.
const QString INDENTATION_DELTA
QString QTransformToString(const QTransform &transform)
QString QPointFToString(const QPointF &pos)
ostream & operator<<(ostream &strOuter, const Transformation &transformation)
const int PRECISION_DIGITS
Max number of significant digits.
const double ZERO_OFFSET_AFTER_LOG
QTransform matrixGraph() const
Returns graph coordinates matrix after transformIsDefined has already indicated success.
CallbackSearchReturn callback(const QString &curveName, const Point &point)
Callback method.
QTransform matrixScreen() const
Returns screen coordinates matrix after transformIsDefined has already indicated success.
Callback for collecting axis points and then calculating the current transform from those axis points...
bool transformIsDefined() const
True if enough Points were available to create a Transformation.
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.
Model for DlgSettingsGeneral and CmdSettingsGeneral.
DocumentModelGeneral modelGeneral() const
Get method for DocumentModelGeneral.
Definition Document.cpp:735
DocumentModelCoords modelCoords() const
Get method for DocumentModelCoords.
Definition Document.cpp:707
DocumentAxesPointsRequired documentAxesPointsRequired() const
Get method for DocumentAxesPointsRequired.
Definition Document.cpp:369
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.