Engauge Digitizer 2
Loading...
Searching...
No Matches
Checker.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
7#include "Checker.h"
9#include "EngaugeAssert.h"
10#include "EnumsToQt.h"
11#include "GridLineFactory.h"
12#include "Logger.h"
13#include "mmsubs.h"
14#include <QDebug>
15#include <QGraphicsItem>
16#include <QGraphicsScene>
17#include <qmath.h>
18#include <QPen>
19#include <QTextStream>
20#include "QtToString.h"
21#include "Transformation.h"
22
23const int NUM_AXES_POINTS_2 = 2;
24const int NUM_AXES_POINTS_3 = 3;
25const int NUM_AXES_POINTS_4 = 4;
26
27extern const QString DUMMY_CURVE_NAME;
28
29// One-pixel wide line (produced by setting width=0) is too small. 5 is big enough to be always noticeable,
30// but such a thick line obscures the axes points. To keep the axes points visible, we remove portions of
31// the line nearer to an axes point than the point radius.
33
34Checker::Checker(QGraphicsScene &scene) :
35 m_scene (scene)
36{
37}
38
40{
41 m_gridLines.clear ();
42}
43
44void Checker::adjustPolarAngleRanges (const DocumentModelCoords &modelCoords,
45 const Transformation &transformation,
46 const QList<Point> &points,
47 double &xMin,
48 double &xMax,
49 double &yMin) const
50{
51 LOG4CPP_INFO_S ((*mainCat)) << "Checker::adjustPolarAngleRanges transformation=" << transformation;
52
53 const double UNIT_LENGTH = 1.0;
54
55 QString path; // For logging
56 if (modelCoords.coordsType() == COORDS_TYPE_POLAR) {
57
58 // Range minimum is at origin
59 yMin = modelCoords.originRadius();
60
61 path = QString ("yMin=%1 ").arg (yMin); // For logging
62
63 // Perform special processing to account for periodicity of polar coordinates. Start with unit vectors
64 // in the directions of the three axis points
65 double angle0 = points.at(0).posGraph().x();
66 double angle1 = points.at(1).posGraph().x();
67 double angle2 = points.at(2).posGraph().x();
68 QPointF pos0 = transformation.cartesianFromCartesianOrPolar(modelCoords,
69 QPointF (angle0, UNIT_LENGTH));
70 QPointF pos1 = transformation.cartesianFromCartesianOrPolar(modelCoords,
71 QPointF (angle1, UNIT_LENGTH));
72 QPointF pos2 = transformation.cartesianFromCartesianOrPolar(modelCoords,
73 QPointF (angle2, UNIT_LENGTH));
74
75 // Identify the axis point that is more in the center of the other two axis points. The arc is then drawn
76 // from one of the other two points to the other. Center point has smaller angles with the other points
77 double sumAngle0 = angleBetweenVectors(pos0, pos1) + angleBetweenVectors(pos0, pos2);
78 double sumAngle1 = angleBetweenVectors(pos1, pos0) + angleBetweenVectors(pos1, pos2);
79 double sumAngle2 = angleBetweenVectors(pos2, pos0) + angleBetweenVectors(pos2, pos1);
80 if ((sumAngle0 <= sumAngle1) && (sumAngle0 <= sumAngle2)) {
81
82 // Point 0 is in the middle. Either or neither of points 1 and 2 may be along point 0
83 if ((angleFromVectorToVector (pos0, pos1) < 0) ||
84 (angleFromVectorToVector (pos0, pos2) > 0)) {
85 path += QString ("from 1=%1 through 0 to 2=%2").arg (angle1).arg (angle2);
86 xMin = angle1;
87 xMax = angle2;
88 } else {
89 path += QString ("from 2=%1 through 0 to 1=%2").arg (angle2).arg (angle1);
90 xMin = angle2;
91 xMax = angle1;
92 }
93 } else if ((sumAngle1 <= sumAngle0) && (sumAngle1 <= sumAngle2)) {
94
95 // Point 1 is in the middle. Either or neither of points 0 and 2 may be along point 1
96 if ((angleFromVectorToVector (pos1, pos0) < 0) ||
97 (angleFromVectorToVector (pos1, pos2) > 0)) {
98 path += QString ("from 0=%1 through 1 to 2=%2").arg (angle0).arg (angle2);
99 xMin = angle0;
100 xMax = angle2;
101 } else {
102 path += QString ("from 2=%1 through 1 to 0=%2").arg (angle2).arg (angle0);
103 xMin = angle2;
104 xMax = angle0;
105 }
106 } else {
107
108 // Point 2 is in the middle. Either or neither of points 0 and 1 may be along point 2
109 if ((angleFromVectorToVector (pos2, pos0) < 0) ||
110 (angleFromVectorToVector (pos2, pos1) > 0)) {
111 path += QString ("from 0=%1 through 2 to 1=%2").arg (angle0).arg (angle1);
112 xMin = angle0;
113 xMax = angle1;
114 } else {
115 path += QString ("from 1=%1 through 2 to 0=%2").arg (angle1).arg (angle0);
116 xMin = angle1;
117 xMax = angle0;
118 }
119 }
120
121 // Make sure theta is increasing
122 while (xMax < xMin) {
123
124 double thetaPeriod = modelCoords.thetaPeriod();
125
126 path += QString (" xMax+=%1").arg (thetaPeriod);
127 xMax += thetaPeriod;
128
129 }
130 }
131
132 LOG4CPP_INFO_S ((*mainCat)) << "Checker::adjustPolarAngleRanges path=(" << path.toLatin1().data() << ")";
133}
134
135void Checker::prepareForDisplay (const QPolygonF &polygon,
136 int pointRadius,
137 const DocumentModelAxesChecker &modelAxesChecker,
138 const DocumentModelCoords &modelCoords,
139 DocumentAxesPointsRequired documentAxesPointsRequired)
140{
141 LOG4CPP_INFO_S ((*mainCat)) << "Checker::prepareForDisplay";
142
143 ENGAUGE_ASSERT ((polygon.count () == NUM_AXES_POINTS_2) ||
144 (polygon.count () == NUM_AXES_POINTS_3) ||
145 (polygon.count () == NUM_AXES_POINTS_4));
146
147 // Convert pixel coordinates in QPointF to screen and graph coordinates in Point using
148 // identity transformation, so this routine can reuse computations provided by Transformation
149 QList<Point> points;
150 QPolygonF::const_iterator itr;
151 for (itr = polygon.begin (); itr != polygon.end (); itr++) {
152
153 const QPointF &pF = *itr;
154
156 pF,
157 pF,
158 false);
159 points.push_back (p);
160 }
161
162 // Screen and graph coordinates are treated as the same, so identity transform is used
163 Transformation transformIdentity;
164 transformIdentity.identity();
165 prepareForDisplay (points,
166 pointRadius,
167 modelAxesChecker,
168 modelCoords,
169 transformIdentity,
170 documentAxesPointsRequired);
171}
172
173void Checker::prepareForDisplay (const QList<Point> &points,
174 int pointRadius,
175 const DocumentModelAxesChecker &modelAxesChecker,
176 const DocumentModelCoords &modelCoords,
177 const Transformation &transformation,
178 DocumentAxesPointsRequired documentAxesPointsRequired)
179{
180 LOG4CPP_INFO_S ((*mainCat)) << "Checker::prepareForDisplay "
181 << " transformation=" << transformation;
182
183 ENGAUGE_ASSERT ((points.count () == NUM_AXES_POINTS_2) ||
184 (points.count () == NUM_AXES_POINTS_3) ||
185 (points.count () == NUM_AXES_POINTS_4));
186
187 // Remove previous lines
188 m_gridLines.clear ();
189
190 bool fourPoints = (documentAxesPointsRequired == DOCUMENT_AXES_POINTS_REQUIRED_4);
191
192 // Get the min and max of x and y. We initialize yTo to prevent compiler warning
193 double xFrom = 0, xTo = 0, yFrom = 0, yTo = 0;
194 int i;
195 bool firstX = true;
196 bool firstY = true;
197 for (i = 0; i < points.count(); i++) {
198 if (!fourPoints || (points.at(i).isXOnly() && fourPoints)) {
199
200 // X coordinate is defined
201 if (firstX) {
202 xFrom = points.at(i).posGraph().x();
203 xTo = points.at(i).posGraph().x();
204 firstX = false;
205 } else {
206 xFrom = qMin (xFrom, points.at(i).posGraph().x());
207 xTo = qMax (xTo , points.at(i).posGraph().x());
208 }
209 }
210
211 if (!fourPoints || (!points.at(i).isXOnly() && fourPoints)) {
212
213 // Y coordinate is defined
214 if (firstY) {
215 yFrom = points.at(i).posGraph().y();
216 yTo = points.at(i).posGraph().y();
217 firstY = false;
218 } else {
219 yFrom = qMin (yFrom, points.at(i).posGraph().y());
220 yTo = qMax (yTo , points.at(i).posGraph().y());
221 }
222 }
223 }
224
225 // Min and max of angles needs special processing since periodicity introduces some ambiguity. This is a noop for rectangular coordinates
226 // and for polar coordinates when periodicity is not an issue
227 adjustPolarAngleRanges (modelCoords,
228 transformation,
229 points,
230 xFrom,
231 xTo,
232 yFrom);
233
234 // Draw the bounding box as four sides. In polar plots the bottom side is zero-length, with pie shape resulting
235 GridLineFactory factory (m_scene,
236 pointRadius,
237 points,
238 modelCoords);
239 m_gridLines.add (factory.createGridLine (xFrom, yFrom, xFrom, yTo , transformation));
240 m_gridLines.add (factory.createGridLine (xFrom, yTo , xTo , yTo , transformation));
241 m_gridLines.add (factory.createGridLine (xTo , yTo , xTo , yFrom, transformation));
242 m_gridLines.add (factory.createGridLine (xTo , yFrom, xFrom, yFrom, transformation));
243
244 updateModelAxesChecker (modelAxesChecker);
245}
246
247void Checker::setVisible (bool visible)
248{
249 m_gridLines.setVisible (visible);
250}
251
253{
254 QColor color = ColorPaletteToQColor (modelAxesChecker.lineColor());
255 QPen pen (QBrush (color), CHECKER_POINTS_WIDTH);
256
257 m_gridLines.setPen (pen);
258}
const int CHECKER_POINTS_WIDTH
Definition Checker.cpp:32
const int NUM_AXES_POINTS_2
Definition Checker.cpp:23
const QString DUMMY_CURVE_NAME
const int NUM_AXES_POINTS_4
Definition Checker.cpp:25
const int NUM_AXES_POINTS_3
Definition Checker.cpp:24
@ COORDS_TYPE_POLAR
Definition CoordsType.h:14
@ DOCUMENT_AXES_POINTS_REQUIRED_4
#define ENGAUGE_ASSERT(cond)
Drop in replacement for Q_ASSERT if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS) define ENGAUGE...
QColor ColorPaletteToQColor(ColorPalette color)
Definition EnumsToQt.cpp:15
log4cpp::Category * mainCat
Definition Logger.cpp:14
virtual ~Checker()
Definition Checker.cpp:39
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:135
Checker(QGraphicsScene &scene)
Single constructor for DlgSettingsAxesChecker, which does not have an explicit transformation....
Definition Checker.cpp:34
void setVisible(bool visible)
Show/hide this axes checker.
Definition Checker.cpp:247
virtual void updateModelAxesChecker(const DocumentModelAxesChecker &modelAxesChecker)
Apply the new DocumentModelAxesChecker, to the points already associated with this object.
Definition Checker.cpp:252
Model for DlgSettingsAxesChecker and CmdSettingsAxesChecker.
ColorPalette lineColor() const
Get method for line color.
Model for DlgSettingsCoords and CmdSettingsCoords.
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.
Factory class for generating the points, composed of QGraphicsItem objects, along a GridLine.
GridLine * createGridLine(double xFrom, double yFrom, double xTo, double yTo, const Transformation &transformation)
Create grid line, either along constant X/theta or constant Y/radius side.
void setVisible(bool visible)
Make all grid lines visible or hidden.
Definition GridLines.cpp:41
void add(GridLine *gridLine)
Add specified grid line. Ownership of all allocated QGraphicsItems is passed to new GridLine.
Definition GridLines.cpp:19
void clear()
Deallocate and remove all grid lines.
Definition GridLines.cpp:24
void setPen(const QPen &pen)
Set the pen style of each grid line.
Definition GridLines.cpp:34
Class that represents one digitized point. The screen-to-graph coordinate transformation is always ex...
Definition Point.h:26
Affine transformation between screen and graph coordinates, based on digitized axis points.
void identity()
Identity transformation.
static QPointF cartesianFromCartesianOrPolar(const DocumentModelCoords &modelCoords, const QPointF &posGraphIn)
Output cartesian coordinates from input cartesian or polar coordinates. This is static for easier use...
#define LOG4CPP_INFO_S(logger)
Definition convenience.h:18
double angleFromVectorToVector(const QPointF &vFrom, const QPointF &vTo)
Angle between two vectors. Direction is positive when rotation is about +z vector,...
Definition mmsubs.cpp:32
double angleBetweenVectors(const QPointF &v1, const QPointF &v2)
Angle between two vectors. Direction is unimportant, so result is between 0 to pi radians.
Definition mmsubs.cpp:15