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