Engauge Digitizer  2
Checker.cpp
1 #include "Checker.h"
2 #include "EngaugeAssert.h"
3 #include "EnumsToQt.h"
4 #include "GraphicsArcItem.h"
5 #include "Logger.h"
6 #include "mmsubs.h"
7 #include <QDebug>
8 #include <QGraphicsScene>
9 #include <qmath.h>
10 #include <QPen>
11 #include <QTextStream>
12 #include "QtToString.h"
13 #include "Transformation.h"
14 
15 const int NUM_AXES_POINTS = 3;
16 
17 extern const QString DUMMY_CURVE_NAME;
18 const int Z_VALUE_IN_FRONT = 100;
19 const int NO_SIDE = -1;
20 
21 // To emphasize that the axis lines are still there, we make these checker somewhat transparent
22 const double CHECKER_OPACITY = 0.6;
23 
24 // One-pixel wide line (produced by setting width=0) is too small. 5 is big enough to be always noticeable,
25 // but such a thick line obscures the axes points. To keep the axes points visible, we remove portions of
26 // the line nearer to an axes point than the point radius.
27 const int CHECKER_POINTS_WIDTH = 5;
28 
29 const double PI = 3.1415926535;
30 const double TWO_PI = 2.0 * PI;
31 const double DEGREES_TO_RADIANS = PI / 180.0;
32 const double RADIANS_TO_TICS = 5760 / TWO_PI;
33 const double RADIANS_TO_DEGREES = 180.0 / PI;
34 
35 Checker::Checker(QGraphicsScene &scene) :
36  m_scene (scene)
37 {
38 }
39 
40 void Checker::adjustPolarAngleRanges (const DocumentModelCoords &modelCoords,
41  const Transformation &transformation,
42  const QList<Point> &points,
43  double &xMin,
44  double &xMax,
45  double &yMin) const
46 {
47  LOG4CPP_INFO_S ((*mainCat)) << "Checker::adjustPolarAngleRanges transformation=" << transformation;
48 
49  const double UNIT_LENGTH = 1.0;
50 
51  QString path; // For logging
52  if (modelCoords.coordsType() == COORDS_TYPE_POLAR) {
53 
54  // Range minimum is at origin
55  yMin = modelCoords.originRadius();
56 
57  path = QString ("yMin=%1 ").arg (yMin); // For logging
58 
59  // Perform special processing to account for periodicity of polar coordinates. Start with unit vectors
60  // in the directions of the three axis points
61  double angle0 = points.at(0).posGraph().x();
62  double angle1 = points.at(1).posGraph().x();
63  double angle2 = points.at(2).posGraph().x();
64  QPointF pos0 = transformation.cartesianFromCartesianOrPolar(modelCoords,
65  QPointF (angle0, UNIT_LENGTH));
66  QPointF pos1 = transformation.cartesianFromCartesianOrPolar(modelCoords,
67  QPointF (angle1, UNIT_LENGTH));
68  QPointF pos2 = transformation.cartesianFromCartesianOrPolar(modelCoords,
69  QPointF (angle2, UNIT_LENGTH));
70 
71  // Identify the axis point that is more in the center of the other two axis points. The arc is then drawn
72  // from one of the other two points to the other. Center point has smaller angles with the other points
73  double sumAngle0 = angleBetweenVectors(pos0, pos1) + angleBetweenVectors(pos0, pos2);
74  double sumAngle1 = angleBetweenVectors(pos1, pos0) + angleBetweenVectors(pos1, pos2);
75  double sumAngle2 = angleBetweenVectors(pos2, pos0) + angleBetweenVectors(pos2, pos1);
76  if ((sumAngle0 <= sumAngle1) && (sumAngle0 <= sumAngle2)) {
77 
78  // Point 0 is in the middle. Either or neither of points 1 and 2 may be along point 0
79  if ((angleFromVectorToVector (pos0, pos1) < 0) ||
80  (angleFromVectorToVector (pos0, pos2) > 0)) {
81  path += QString ("from 1=%1 through 0 to 2=%2").arg (angle1).arg (angle2);
82  xMin = angle1;
83  xMax = angle2;
84  } else {
85  path += QString ("from 2=%1 through 0 to 1=%2").arg (angle2).arg (angle1);
86  xMin = angle2;
87  xMax = angle1;
88  }
89  } else if ((sumAngle1 <= sumAngle0) && (sumAngle1 <= sumAngle2)) {
90 
91  // Point 1 is in the middle. Either or neither of points 0 and 2 may be along point 1
92  if ((angleFromVectorToVector (pos1, pos0) < 0) ||
93  (angleFromVectorToVector (pos1, pos2) > 0)) {
94  path += QString ("from 0=%1 through 1 to 2=%2").arg (angle0).arg (angle2);
95  xMin = angle0;
96  xMax = angle2;
97  } else {
98  path += QString ("from 2=%1 through 1 to 0=%2").arg (angle2).arg (angle0);
99  xMin = angle2;
100  xMax = angle0;
101  }
102  } else {
103 
104  // Point 2 is in the middle. Either or neither of points 0 and 1 may be along point 2
105  if ((angleFromVectorToVector (pos2, pos0) < 0) ||
106  (angleFromVectorToVector (pos2, pos1) > 0)) {
107  path += QString ("from 0=%1 through 2 to 1=%2").arg (angle0).arg (angle1);
108  xMin = angle0;
109  xMax = angle1;
110  } else {
111  path += QString ("from 1=%1 through 2 to 0=%2").arg (angle1).arg (angle0);
112  xMin = angle1;
113  xMax = angle0;
114  }
115  }
116 
117  // Make sure theta is increasing
118  while (xMax < xMin) {
119 
120  double thetaPeriod = modelCoords.thetaPeriod();
121 
122  path += QString (" xMax+=%1").arg (thetaPeriod);
123  xMax += thetaPeriod;
124 
125  }
126  }
127 
128  LOG4CPP_INFO_S ((*mainCat)) << "Checker::adjustPolarAngleRanges path=(" << path.toLatin1().data() << ")";
129 }
130 
131 void Checker::bindItemToScene(QGraphicsItem *item) const
132 {
133  LOG4CPP_DEBUG_S ((*mainCat)) << "Checker::bindItemToScene";
134 
135  item->setOpacity (CHECKER_OPACITY);
136  item->setZValue (Z_VALUE_IN_FRONT);
137  item->setToolTip (QObject::tr ("Axes checker. If this does not align with the axes, then the axes points should be checked"));
138 
139  m_scene.addItem (item);
140 }
141 
142 void Checker::createSide (int pointRadius,
143  const QList<Point> &points,
144  const DocumentModelCoords &modelCoords,
145  double xFrom,
146  double yFrom,
147  double xTo,
148  double yTo,
149  const Transformation &transformation,
150  SideSegments &sideSegments)
151 {
152  LOG4CPP_INFO_S ((*mainCat)) << "Checker::createSide"
153  << " pointRadius=" << pointRadius
154  << " xFrom=" << xFrom
155  << " yFrom=" << yFrom
156  << " xTo=" << xTo
157  << " yTo=" << yTo
158  << " transformation=" << transformation;
159 
160  // Originally a complicated algorithm tried to intercept a straight line from (xFrom,yFrom) to (xTo,yTo). That did not work well since:
161  // 1) Calculations for mostly orthogonal cartesian coordinates worked less well with non-orthogonal polar coordinates
162  // 2) Ambiguity in polar coordinates between the shorter and longer paths between (theta0,radius) and (theta1,radius)
163  //
164  // Current algorithm breaks up the interval between (xMin,yMin) and (xMax,yMax) into many smaller pieces and stitches the
165  // desired pieces together. For straight lines in linear graphs this algorithm is very much overkill, but there is no significant
166  // penalty and this approach works in every situation
167 
168  // Should give single-pixel resolution on most images, and 'good enough' resolution on extremely large images
169  const int NUM_STEPS = 1000;
170 
171  bool stateSegmentIsActive = false;
172  QPointF posStartScreen (0, 0);
173 
174  // Loop through steps. Final step i=NUM_STEPS does final processing if a segment is active
175  for (int i = 0; i <= NUM_STEPS; i++) {
176 
177  double s = (double) i / (double) NUM_STEPS;
178 
179  // Interpolate coordinates assuming normal linear scaling
180  double xGraph = (1.0 - s) * xFrom + s * xTo;
181  double yGraph = (1.0 - s) * yFrom + s * yTo;
182 
183  // Replace interpolated coordinates using log scaling if appropriate, preserving the same ranges
184  if (modelCoords.coordScaleXTheta() == COORD_SCALE_LOG) {
185  xGraph = qExp ((1.0 - s) * qLn (xFrom) + s * qLn (xTo));
186  }
187  if (modelCoords.coordScaleYRadius() == COORD_SCALE_LOG) {
188  yGraph = qExp ((1.0 - s) * qLn (yFrom) + s * qLn (yTo));
189  }
190 
191  QPointF pointScreen;
192  transformation.transformRawGraphToScreen (QPointF (xGraph, yGraph),
193  pointScreen);
194 
195  double distanceToNearestPoint = minScreenDistanceFromPoints (pointScreen,
196  points);
197  if ((distanceToNearestPoint < pointRadius) ||
198  (i == NUM_STEPS)) {
199 
200  // Too close to point, so point is not included in side. Or this is the final iteration of the loop
201  if (stateSegmentIsActive) {
202 
203  // State transition
204  finishActiveSegment (modelCoords,
205  posStartScreen,
206  pointScreen,
207  yFrom,
208  yTo,
209  transformation,
210  sideSegments);
211  stateSegmentIsActive = false;
212 
213  }
214  } else {
215 
216  // Outside point, so include point in side
217  if (!stateSegmentIsActive) {
218 
219  // State transition
220  stateSegmentIsActive = true;
221  posStartScreen = pointScreen;
222 
223  }
224  }
225  }
226 }
227 
228 void Checker::createTransformAlign (const Transformation &transformation,
229  double radiusLinearCartesian,
230  const QPointF &posOriginScreen,
231  QTransform &transformAlign,
232  double &ellipseXAxis,
233  double &ellipseYAxis) const
234 {
235  // LOG4CPP_INFO_S is below
236 
237  // Compute a minimal transformation that aligns the graph x and y axes with the screen x and y axes. Specifically, shear,
238  // translation and rotation are allowed but not scaling. Scaling is bad since it messes up the line thickness of the drawn arc.
239  //
240  // Assumptions:
241  // 1) Keep the graph origin at the same screen coordinates
242  // 2) Keep the (+radius,0) the same pixel distance from the origin but moved to the same pixel row as the origin
243  // 3) Keep the (0,+radius) the same pixel distance from the origin but moved to the same pixel column as the origin
244 
245  // Get (+radius,0) and (0,+radius) points
246  QPointF posXRadiusY0Graph (radiusLinearCartesian, 0), posX0YRadiusGraph (0, radiusLinearCartesian);
247  QPointF posXRadiusY0Screen, posX0YRadiusScreen;
248  transformation.transformLinearCartesianGraphToScreen (posXRadiusY0Graph,
249  posXRadiusY0Screen);
250  transformation.transformLinearCartesianGraphToScreen (posX0YRadiusGraph,
251  posX0YRadiusScreen);
252 
253  // Compute arc/ellipse parameters
254  QPointF deltaXRadiusY0 = posXRadiusY0Screen - posOriginScreen;
255  QPointF deltaX0YRadius = posX0YRadiusScreen - posOriginScreen;
256  ellipseXAxis = qSqrt (deltaXRadiusY0.x () * deltaXRadiusY0.x () +
257  deltaXRadiusY0.y () * deltaXRadiusY0.y ());
258  ellipseYAxis = qSqrt (deltaX0YRadius.x () * deltaX0YRadius.x () +
259  deltaX0YRadius.y () * deltaX0YRadius.y ());
260 
261  // Compute the aligned coordinates, constrained by the rules listed above
262  QPointF posXRadiusY0AlignedScreen (posOriginScreen.x() + ellipseXAxis, posOriginScreen.y());
263  QPointF posX0YRadiusAlignedScreen (posOriginScreen.x(), posOriginScreen.y() - ellipseYAxis);
264 
265  transformAlign = Transformation::calculateTransformFromLinearCartesianPoints (posOriginScreen,
266  posXRadiusY0Screen,
267  posX0YRadiusScreen,
268  posOriginScreen,
269  posXRadiusY0AlignedScreen,
270  posX0YRadiusAlignedScreen);
271 
272  LOG4CPP_INFO_S ((*mainCat)) << "Checker::createTransformAlign"
273  << " transformation=" << QTransformToString (transformation.transformMatrix()).toLatin1().data() << endl
274  << " radiusLinearCartesian=" << radiusLinearCartesian
275  << " posXRadiusY0Screen=" << QPointFToString (posXRadiusY0Screen).toLatin1().data()
276  << " posX0YRadiusScreen=" << QPointFToString (posX0YRadiusScreen).toLatin1().data()
277  << " ellipseXAxis=" << ellipseXAxis
278  << " ellipseYAxis=" << ellipseYAxis
279  << " posXRadiusY0AlignedScreen=" << QPointFToString (posXRadiusY0AlignedScreen).toLatin1().data()
280  << " posX0YRadiusAlignedScreen=" << QPointFToString (posX0YRadiusAlignedScreen).toLatin1().data()
281  << " transformAlign=" << QTransformToString (transformAlign).toLatin1().data();
282 }
283 
284 void Checker::deleteSide (SideSegments &sideSegments)
285 {
286  for (int i = 0; i < sideSegments.count(); i++) {
287  QGraphicsItem *item = sideSegments [i];
288  if (item != 0) {
289  delete item;
290  }
291  }
292 
293  sideSegments.clear();
294 }
295 
296 QGraphicsItem *Checker::ellipseItem(const Transformation &transformation,
297  double radiusLinearCartesian,
298  const QPointF &posStartScreen,
299  const QPointF &posEndScreen) const
300 {
301  // LOG4CPP_INFO_S is below
302 
303  QPointF posStartGraph, posEndGraph;
304 
305  transformation.transformScreenToRawGraph (posStartScreen,
306  posStartGraph);
307  transformation.transformScreenToRawGraph (posEndScreen,
308  posEndGraph);
309 
310  // Get the angles about the origin of the start and end points
311  double angleStart = posStartGraph.x() * DEGREES_TO_RADIANS;
312  double angleEnd = posEndGraph.x() * DEGREES_TO_RADIANS;
313  if (angleEnd < angleStart) {
314  angleEnd += TWO_PI;
315  }
316  double angleSpan = angleEnd - angleStart;
317 
318  // Get origin
319  QPointF posOriginGraph (0, 0), posOriginScreen;
320  transformation.transformLinearCartesianGraphToScreen (posOriginGraph,
321  posOriginScreen);
322 
323  LOG4CPP_INFO_S ((*mainCat)) << "Checker::ellipseItem"
324  << " radiusLinearCartesian=" << radiusLinearCartesian
325  << " posStartScreen=" << QPointFToString (posStartScreen).toLatin1().data()
326  << " posEndScreen=" << QPointFToString (posEndScreen).toLatin1().data()
327  << " posOriginScreen=" << QPointFToString (posOriginScreen).toLatin1().data()
328  << " angleStart=" << angleStart / DEGREES_TO_RADIANS
329  << " angleEnd=" << angleEnd / DEGREES_TO_RADIANS
330  << " transformation=" << transformation;
331 
332  // Compute rotate/shear transform that aligns linear cartesian graph coordinates with screen coordinates, and ellipse parameters.
333  // Transform does not include scaling since that messes up the thickness of the drawn line, and does not include
334  // translation since that is not important
335  double ellipseXAxis, ellipseYAxis;
336  QTransform transformAlign;
337  createTransformAlign (transformation,
338  radiusLinearCartesian,
339  posOriginScreen,
340  transformAlign,
341  ellipseXAxis,
342  ellipseYAxis);
343 
344  // Create a circle in graph space with the specified radius
345  QRectF boundingRect (-1.0 * ellipseXAxis + posOriginScreen.x(),
346  -1.0 * ellipseYAxis + posOriginScreen.y(),
347  2 * ellipseXAxis,
348  2 * ellipseYAxis);
349  GraphicsArcItem *item = new GraphicsArcItem (boundingRect);
350  item->setStartAngle (angleStart * RADIANS_TO_TICS);
351  item->setSpanAngle (angleSpan * RADIANS_TO_TICS);
352 
353  item->setTransform (transformAlign.transposed ().inverted ());
354 
355  return item;
356 }
357 
358 void Checker::finishActiveSegment (const DocumentModelCoords &modelCoords,
359  const QPointF &posStartScreen,
360  const QPointF &posEndScreen,
361  double yFrom,
362  double yTo,
363  const Transformation &transformation,
364  SideSegments &sideSegments) const
365 {
366  LOG4CPP_INFO_S ((*mainCat)) << "Checker::finishActiveSegment"
367  << " posStartScreen=" << QPointFToString (posStartScreen).toLatin1().data()
368  << " posEndScreen=" << QPointFToString (posEndScreen).toLatin1().data()
369  << " yFrom=" << yFrom
370  << " yTo=" << yTo;
371 
372  QGraphicsItem *item;
373  if ((modelCoords.coordsType() == COORDS_TYPE_POLAR) &&
374  (yFrom == yTo)) {
375 
376  // Linear cartesian radius
377  double radiusLinearCartesian = yFrom;
378  if (modelCoords.coordScaleYRadius() == COORD_SCALE_LOG) {
379  radiusLinearCartesian = transformation.logToLinearRadius(yFrom,
380  modelCoords.originRadius());
381  } else {
382  radiusLinearCartesian -= modelCoords.originRadius();
383  }
384 
385  // Draw along an arc since this is a side of constant radius, and we have polar coordinates
386  item = ellipseItem (transformation,
387  radiusLinearCartesian,
388  posStartScreen,
389  posEndScreen);
390 
391  } else {
392 
393  // Draw straight line
394  item = lineItem (posStartScreen,
395  posEndScreen);
396  }
397 
398  sideSegments.push_back (item);
399  bindItemToScene (item);
400 }
401 
402 QGraphicsItem *Checker::lineItem (const QPointF &posStartScreen,
403  const QPointF &posEndScreen) const
404 {
405  LOG4CPP_INFO_S ((*mainCat)) << "Checker::lineItem"
406  << " posStartScreen=" << QPointFToString (posStartScreen).toLatin1().data()
407  << " posEndScreen=" << QPointFToString (posEndScreen).toLatin1().data();
408 
409  return new QGraphicsLineItem (QLineF (posStartScreen,
410  posEndScreen));
411 }
412 
413 double Checker::minScreenDistanceFromPoints (const QPointF &posScreen,
414  const QList<Point> &points)
415 {
416  double minDistance = 0;
417  for (int i = 0; i < points.count (); i++) {
418  const Point &pointCenter = points.at (i);
419 
420  double dx = posScreen.x() - pointCenter.posScreen().x();
421  double dy = posScreen.y() - pointCenter.posScreen().y();
422 
423  double distance = qSqrt (dx * dx + dy * dy);
424  if (i == 0 || distance < minDistance) {
425  minDistance = distance;
426  }
427  }
428 
429  return minDistance;
430 }
431 
432 void Checker::prepareForDisplay (const QPolygonF &polygon,
433  int pointRadius,
434  const DocumentModelAxesChecker &modelAxesChecker,
435  const DocumentModelCoords &modelCoords)
436 {
437  LOG4CPP_INFO_S ((*mainCat)) << "Checker::prepareForDisplay";
438 
439  ENGAUGE_ASSERT (polygon.count () == NUM_AXES_POINTS);
440 
441  // Convert pixel coordinates in QPointF to screen and graph coordinates in Point using
442  // identity transformation, so this routine can reuse computations provided by Transformation
443  QList<Point> points;
444  QPolygonF::const_iterator itr;
445  for (itr = polygon.begin (); itr != polygon.end (); itr++) {
446 
447  const QPointF &pF = *itr;
448 
449  Point p (DUMMY_CURVE_NAME,
450  pF,
451  pF);
452  points.push_back (p);
453  }
454 
455  // Screen and graph coordinates are treated as the same, so identity transform is used
456  Transformation transformIdentity;
457  transformIdentity.identity();
458  prepareForDisplay (points,
459  pointRadius,
460  modelAxesChecker,
461  modelCoords,
462  transformIdentity);
463 }
464 
465 void Checker::prepareForDisplay (const QList<Point> &points,
466  int pointRadius,
467  const DocumentModelAxesChecker &modelAxesChecker,
468  const DocumentModelCoords &modelCoords,
469  const Transformation &transformation)
470 {
471  LOG4CPP_INFO_S ((*mainCat)) << "Checker::prepareForDisplay "
472  << " transformation=" << transformation;
473 
474  ENGAUGE_ASSERT (points.count () == NUM_AXES_POINTS);
475 
476  // Remove previous lines
477  deleteSide (m_sideLeft);
478  deleteSide (m_sideTop);
479  deleteSide (m_sideRight);
480  deleteSide (m_sideBottom);
481 
482  // Get the min and max of x and y. We initialize yTo to prevent compiler warning
483  double xFrom, xTo, yFrom, yTo = 0;
484  int i;
485  for (i = 0; i < NUM_AXES_POINTS; i++) {
486  if (i == 0) {
487  xFrom = points.at(i).posGraph().x();
488  xTo = points.at(i).posGraph().x();
489  yFrom = points.at(i).posGraph().y();
490  yTo = points.at(i).posGraph().y();
491  }
492  xFrom = qMin (xFrom, points.at(i).posGraph().x());
493  xTo = qMax (xTo , points.at(i).posGraph().x());
494  yFrom = qMin (yFrom, points.at(i).posGraph().y());
495  yTo = qMax (yTo , points.at(i).posGraph().y());
496  }
497 
498  // Min and max of angles needs special processing since periodicity introduces some ambiguity. This is a noop for rectangular coordinates
499  // and for polar coordinates when periodicity is not an issue
500  adjustPolarAngleRanges (modelCoords,
501  transformation,
502  points,
503  xFrom,
504  xTo,
505  yFrom);
506 
507  // Draw the bounding box as four sides. In polar plots the bottom side is zero-length, with pie shape resulting
508  createSide (pointRadius, points, modelCoords, xFrom, yFrom, xFrom, yTo , transformation, m_sideLeft);
509  createSide (pointRadius, points, modelCoords, xFrom, yTo , xTo , yTo , transformation, m_sideTop);
510  createSide (pointRadius, points, modelCoords, xTo , yTo , xTo , yFrom, transformation, m_sideRight);
511  createSide (pointRadius, points, modelCoords, xTo , yFrom, xFrom, yFrom, transformation, m_sideBottom);
512 
513  updateModelAxesChecker (modelAxesChecker);
514 }
515 
516 void Checker::setLineColor (SideSegments &sideSegments,
517  const QPen &pen)
518 {
519  for (int i = 0; i < sideSegments.count(); i++) {
520  QGraphicsItem *item = sideSegments [i];
521  if (item != 0) {
522 
523  // Downcast since QGraphicsItem does not have a pen
524  QGraphicsLineItem *itemLine = dynamic_cast<QGraphicsLineItem*> (item);
525  QGraphicsEllipseItem *itemArc = dynamic_cast<QGraphicsEllipseItem*> (item);
526  if (itemLine != 0) {
527  itemLine->setPen (pen);
528  } else if (itemArc != 0) {
529  itemArc->setPen (pen);
530  }
531  }
532  }
533 }
534 
535 void Checker::setVisible (bool visible)
536 {
537  setVisibleSide (m_sideLeft, visible);
538  setVisibleSide (m_sideTop, visible);
539  setVisibleSide (m_sideRight, visible);
540  setVisibleSide (m_sideBottom, visible);
541 }
542 
543 void Checker::setVisibleSide (SideSegments &sideSegments,
544  bool visible)
545 {
546  for (int i = 0; i < sideSegments.count(); i++) {
547  QGraphicsItem *item = sideSegments [i];
548  if (item != 0) {
549  item->setVisible (visible);
550  }
551  }
552 }
553 
555 {
556  QColor color = ColorPaletteToQColor (modelAxesChecker.lineColor());
557  QPen pen (QBrush (color), CHECKER_POINTS_WIDTH);
558 
559  setLineColor (m_sideLeft, pen);
560  setLineColor (m_sideTop, pen);
561  setLineColor (m_sideRight, pen);
562  setLineColor (m_sideBottom, pen);
563 }
void transformScreenToRawGraph(const QPointF &coordScreen, QPointF &coordGraph) const
Transform from cartesian pixel screen coordinates to cartesian/polar graph coordinates.
static QPointF cartesianFromCartesianOrPolar(const DocumentModelCoords &modelCoords, const QPointF &posGraphIn)
Output cartesian coordinates from input cartesian or polar coordinates. This is static for easier use...
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...
QTransform transformMatrix() const
Get method for copying only, for the transform matrix.
void transformLinearCartesianGraphToScreen(const QPointF &coordGraph, QPointF &coordScreen) const
Transform from linear cartesian graph coordinates to cartesian pixel screen coordinates.
static double logToLinearRadius(double r, double rCenter)
Convert radius scaling from log to linear. Calling code is responsible for determining if this is nec...
virtual void updateModelAxesChecker(const DocumentModelAxesChecker &modelAxesChecker)
Apply the new DocumentModelAxesChecker, to the points already associated with this object...
Definition: Checker.cpp:554
CoordScale coordScaleYRadius() const
Get method for linear/log scale on y/radius.
double originRadius() const
Get method for origin radius in polar mode.
Draw an arc as an ellipse but without lines from the center to the start and end points.
ColorPalette lineColor() const
Get method for line color.
Class that represents one digitized point. The screen-to-graph coordinate transformation is always ex...
Definition: Point.h:17
QPointF posScreen() const
Accessor for screen position.
Definition: Point.cpp:344
double thetaPeriod() const
Return the period of the theta value for polar coordinates, consistent with CoordThetaUnits.
Affine transformation between screen and graph coordinates, based on digitized axis points...
CoordScale coordScaleXTheta() const
Get method for linear/log scale on x/theta.
CoordsType coordsType() const
Get method for coordinates type.
Model for DlgSettingsCoords and CmdSettingsCoords.
Model for DlgSettingsAxesChecker and CmdSettingsAxesChecker.
Checker(QGraphicsScene &scene)
Single constructor for DlgSettingsAxesChecker, which does not have an explicit transformation. The identity transformation is assumed.
Definition: Checker.cpp:35
void identity()
Identity transformation.
void setVisible(bool visible)
Show/hide this axes checker.
Definition: Checker.cpp:535
void prepareForDisplay(const QPolygonF &polygon, int pointRadius, const DocumentModelAxesChecker &modelAxesChecker, const DocumentModelCoords &modelCoords)
Create the polygon from current information, including pixel coordinates, just prior to display...
Definition: Checker.cpp:432