Engauge Digitizer 2
Loading...
Searching...
No Matches
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
21const int NUM_AXES_POINTS_3 = 3;
22const int NUM_AXES_POINTS_4 = 4;
23
24extern const QString DUMMY_CURVE_NAME;
25const int Z_VALUE_IN_FRONT = 100;
26
27// To emphasize that the axis lines are still there, we make these checker somewhat transparent
28const 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.
33const int CHECKER_POINTS_WIDTH = 5;
34
35const double PI = 3.1415926535;
36const double TWO_PI = 2.0 * PI;
37const double DEGREES_TO_RADIANS = PI / 180.0;
38const double RADIANS_TO_TICS = 5760 / TWO_PI;
39
40Checker::Checker(QGraphicsScene &scene) :
41 m_scene (scene)
42{
43}
44
45void 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
136void 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
147void 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
233void 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
289void 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
301QGraphicsItem *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
363void 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
407QGraphicsItem *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
418double 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
437void 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
474void 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
546void 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
565void 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
573void 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 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
Checker(QGraphicsScene &scene)
Single constructor for DlgSettingsAxesChecker, which does not have an explicit transformation....
Definition Checker.cpp:40
void setVisible(bool visible)
Show/hide this axes checker.
Definition Checker.cpp:565
virtual void updateModelAxesChecker(const DocumentModelAxesChecker &modelAxesChecker)
Apply the new DocumentModelAxesChecker, to the points already associated with this object.
Definition Checker.cpp:584
Model for DlgSettingsAxesChecker and CmdSettingsAxesChecker.
ColorPalette lineColor() const
Get method for line color.
Model for DlgSettingsCoords and CmdSettingsCoords.
CoordScale coordScaleYRadius() const
Get method for linear/log scale on y/radius.
CoordScale coordScaleXTheta() const
Get method for linear/log scale on x/theta.
double thetaPeriod() const
Return the period of the theta value for polar coordinates, consistent with CoordThetaUnits.
CoordsType coordsType() const
Get method for coordinates type.
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.
Class that represents one digitized point. The screen-to-graph coordinate transformation is always ex...
Definition Point.h:24
QPointF posScreen() const
Accessor for screen position.
Definition Point.cpp:392
Affine transformation between screen and graph coordinates, based on digitized axis points.
void identity()
Identity transformation.
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 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...
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 transformLinearCartesianGraphToScreen(const QPointF &coordGraph, QPointF &coordScreen) const
Transform from linear cartesian graph coordinates to cartesian pixel screen coordinates.