Engauge Digitizer  2
GridInitializer.cpp
1 #include "DocumentModelCoords.h"
2 #include "EngaugeAssert.h"
3 #include "GridInitializer.h"
4 #include "Logger.h"
5 #include <math.h>
6 #include <qmath.h>
7 #include "Transformation.h"
8 
10 {
11 }
12 
13 void GridInitializer::axisScale (double xMin,
14  double xMax,
15  bool linearAxis,
16  double &xStart,
17  double &xStop,
18  double &xDelta,
19  int &xCount) const
20 {
21  const double range_epsilon = 0.00000000001;
22  double xAverage, xAverageRoundedUp, xRange;
23  int nDigitRange;
24 
25  // Define number of digits of precision. although value of 10 seems
26  // desirable, the sprintf statements elsewhere in this file, which
27  // operate on values with the specified precision, just lose it
28  // for more than 8 digits. example '%.7lg' on 40.000005 gives 40.00001
29  const int nDigitsPrecision = 8;
30 
31  // sort the input values
32  if (xMin > xMax) {
33  double xTemp = xMin;
34  xMin = xMax;
35  xMax = xTemp;
36  }
37 
38  // Scale the coordinates logarithmically if log flag is set
39  if (!linearAxis) {
40  ENGAUGE_ASSERT(xMin > 0);
41  ENGAUGE_ASSERT(xMax > 0);
42  xMin = log10(xMin);
43  xMax = log10(xMax);
44  }
45 
46  // Round off average to first significant digit of range
47  xAverage = (xMin + xMax) / 2.0;
48  xRange = xMax - xMin;
49  if (xRange == 0) {
50  xRange = fabs (xAverage / 10.0); // for null range use arbitrary range
51  }
52  nDigitRange = valuePower (xRange);
53  xDelta = pow ((double) 10.0, (double) nDigitRange);
54  xAverageRoundedUp = xDelta * floor ((xAverage + xDelta / 2.0) / xDelta);
55 
56  if (xRange > range_epsilon) {
57  // Adjust stepsize if more points are needed, accounting for roundoff
58  while (fabs (xRange / xDelta) <= 2.000001) {
59  xDelta /= 2.0;
60  }
61  }
62 
63  // Go down until min point is included
64  xStart = xAverageRoundedUp;
65  while (xStart > xMin) {
66  xStart -= xDelta;
67  }
68 
69  // Go up until max point is included
70  xStop = xAverageRoundedUp;
71  while (xStop < xMax) {
72  xStop += xDelta;
73  }
74 
75  xCount = 1 + (int) floor ((xStop - xStart) / xDelta + 0.5);
76 
77  if (!linearAxis) {
78 
79  // Convert from log scale back to linear scale. We make sure to keep numbers like 10^-8 unmolested
80  xStart = pow((double) 10.0, xStart);
81  xStop = pow((double) 10.0, xStop);
82  xDelta = pow((double) 10.0, xDelta);
83 
84  } else {
85 
86  // Roundoff to eliminate epsilons of 10^-10
87  int power = valuePower (xDelta) - nDigitsPrecision;
88  xStart = roundOffToPower(xStart, power);
89  xStop = roundOffToPower(xStop, power);
90  xDelta = roundOffToPower(xDelta, power);
91 
92  }
93 }
94 
95 int GridInitializer::computeCount (bool linearAxis,
96  double start,
97  double stop,
98  double step) const
99 {
100  int count;
101 
102  if (linearAxis) {
103  if (step == 0) {
104  count = 1;
105  } else {
106  count = (int) (1.0 + (stop - start) / step);
107  }
108  } else {
109  if ((start <= 0) || (step <= 0.0)) {
110  count = 1;
111  } else {
112  count = (int) (1.0 + log10 (stop / start) / log10 (step));
113  }
114  }
115 
116  return count;
117 }
118 
119 double GridInitializer::computeStart (bool linearAxis,
120  double stop,
121  double step,
122  int count) const
123 {
124  double start;
125 
126  if (linearAxis) {
127  start = stop - step * (count - 1);
128  } else {
129  start = stop / pow (step, (double) (count - 1));
130  }
131 
132  return start;
133 }
134 
135 double GridInitializer::computeStep (bool linearAxis,
136  double start,
137  double stop,
138  int count) const
139 {
140  double step;
141 
142  if (linearAxis) {
143  if (count > 1) {
144  step = (stop - start) / (count - 1);
145  } else {
146  step = stop - start;
147  }
148  } else {
149  if (start <= 0.0) {
150  step = 1.0;
151  } else {
152  if (count > 1) {
153  step = pow (stop / start, (double) 1.0 / (count - 1));
154  } else {
155  step = stop / start;
156  }
157  }
158  }
159 
160  return step;
161 }
162 
163 double GridInitializer::computeStop (bool linearAxis,
164  double start,
165  double step,
166  int count) const
167 {
168  double stop;
169 
170  if (linearAxis) {
171  stop = start + step * (count - 1);
172  } else {
173  stop = start * pow (step, (double) (count - 1));
174  }
175 
176  return stop;
177 }
178 
180  const DocumentModelCoords &modelCoords) const
181 {
182  LOG4CPP_INFO_S ((*mainCat)) << "GridInitializer::initializeWithNarrowCoverage";
183 
184  DocumentModelGridDisplay modelGridDisplay;
185 
186  int count;
187  double start, stop, step;
188 
189  // X/theta coordinate
190  axisScale (boundingRectGraph.x(),
191  boundingRectGraph.x() + boundingRectGraph.width(),
192  (modelCoords.coordScaleXTheta() == COORD_SCALE_LINEAR),
193  start,
194  stop,
195  step,
196  count);
197 
198  modelGridDisplay.setDisableX (GRID_COORD_DISABLE_COUNT);
199  modelGridDisplay.setCountX (count);
200  modelGridDisplay.setStartX (start);
201  modelGridDisplay.setStepX (step);
202  modelGridDisplay.setStopX (stop);
203 
204  // Y/radius coordinate
205  axisScale (boundingRectGraph.y(),
206  boundingRectGraph.y() + boundingRectGraph.height(),
207  (modelCoords.coordScaleYRadius() == COORD_SCALE_LINEAR),
208  start,
209  stop,
210  step,
211  count);
212 
213  modelGridDisplay.setDisableY (GRID_COORD_DISABLE_COUNT);
214  modelGridDisplay.setCountY (count);
215  modelGridDisplay.setStartY (start);
216  modelGridDisplay.setStepY (step);
217  modelGridDisplay.setStopY (stop);
218 
219  modelGridDisplay.setStable (true);
220 
221  return modelGridDisplay;
222 }
223 
225  const DocumentModelCoords &modelCoords,
226  const Transformation &transformation,
227  const QSize &imageSize) const
228 {
229  LOG4CPP_INFO_S ((*mainCat)) << "GridInitializer::initializeWithWidePolarCoverage";
230 
231  DocumentModelGridDisplay modelGridDisplay = initializeWithNarrowCoverage (boundingRectGraph,
232  modelCoords);
233 
234  if (modelCoords.coordsType() == COORDS_TYPE_POLAR) {
235 
236  overridePolarCoordinateSettings (modelCoords,
237  transformation,
238  modelGridDisplay,
239  imageSize);
240  }
241 
242  return modelGridDisplay;
243 }
244 
245 void GridInitializer::overridePolarCoordinateSettings (const DocumentModelCoords &modelCoords,
246  const Transformation &transformation,
247  DocumentModelGridDisplay &modelGridDisplay,
248  const QSize &imageSize) const
249 {
250  ENGAUGE_ASSERT (modelCoords.coordsType() == COORDS_TYPE_POLAR);
251 
252  // We make sure the angular range is over the entire circle, which is probably useful
253  // unless the orgin is very close to a corner of the graph, in which case the large range does not hurt anything
254  double startX = 0.0;
255  double stopX = 360.0;
256  double stepX = 30.0;
257  int countX = (int) (0.5 + (stopX - startX) / stepX);
258  modelGridDisplay.setStartX (startX);
259  modelGridDisplay.setStepX (stepX);
260  modelGridDisplay.setStopX (stopX);
261  modelGridDisplay.setCountX (countX);
262 
263  // We extend the range to cover the four corners of the image, since otherwise
264  // areas around at least some graph corners are not covered by the grid lines
265  QPointF posTL, posBL, posTR, posBR;
266  transformation.transformScreenToRawGraph (QPointF (0 , imageSize.height ()), posTL);
267  transformation.transformScreenToRawGraph (QPointF (0 , 0 ), posBL);
268  transformation.transformScreenToRawGraph (QPointF (imageSize.width (), imageSize.height ()), posTR);
269  transformation.transformScreenToRawGraph (QPointF (imageSize.width (), 0 ), posBR);
270 
271  double radiusTL = qSqrt (posTL.x () * posTL.x () + posTL.y () * posTL.y ());
272  double radiusBL = qSqrt (posBL.x () * posBL.x () + posBL.y () * posBL.y ());
273  double radiusTR = qSqrt (posTR.x () * posTR.x () + posTR.y () * posTR.y ());
274  double radiusBR = qSqrt (posBR.x () * posBR.x () + posBR.y () * posBR.y ());
275 
276  double radius = qMax (qMax (qMax (radiusTL, radiusBL), radiusTR), radiusBR);
277 
278  double startY = (modelCoords.coordScaleYRadius() == COORD_SCALE_LINEAR ?
279  0.0 :
280  modelCoords.originRadius());
281  double stopY = radius;
282  double stepY = modelGridDisplay.stepY ();
283  int countY = (modelCoords.coordScaleYRadius() == COORD_SCALE_LINEAR ?
284  (int) (0.5 + (stopY - startY) / stepY) :
285  (int) (0.5 + (qLn (stopY) - qLn (startY)) / qLn (stepY)));
286 
287  modelGridDisplay.setStartY (startY);
288  modelGridDisplay.setStopY (stopY);
289  modelGridDisplay.setCountY (countY);
290 }
291 
292 double GridInitializer::roundOffToPower(double arg,
293  int power) const
294 {
295  double powerOf10 = pow ((double) 10, power);
296  return powerOf10 * floor (arg / powerOf10 + 0.5);
297 }
298 
299 int GridInitializer::valuePower(double value) const
300 {
301  const int minPower = -30; // MAX_DOUBLE is 10^38
302 
303  double avalue = fabs(value);
304  if (avalue < pow(10.0, minPower)) {
305  return minPower;
306  } else {
307  return (int) floor (log10 (avalue));
308  }
309 }
void setStartX(double startX)
Set method for x grid line lower bound (inclusive).
Model for DlgSettingsGridDisplay and CmdSettingsGridDisplay.
void setCountY(unsigned int countY)
Set method for y grid line count.
void setStepX(double stepX)
Set method for x grid line increment.
double computeStart(bool linearAxis, double stop, double step, int count) const
Compute axis scale start from the other axis parameters.
void setStepY(double yStep)
Set method for y grid line increment.
int valuePower(double value) const
Compute power of 10 for input value, rounding down to nearest integer solution of value>=10**solution...
void setStable(bool stable)
Set method for stable flag.
Affine transformation between screen and graph coordinates, based on digitized axis points...
DocumentModelGridDisplay initializeWithWidePolarCoverage(const QRectF &boundingRectGraph, const DocumentModelCoords &modelCoords, const Transformation &transformation, const QSize &imageSize) const
Initialize given the boundaries of the graph coordinates, and then extra processing for polar coordin...
GridInitializer()
Single constructor.
DocumentModelGridDisplay initializeWithNarrowCoverage(const QRectF &boundingRectGraph, const DocumentModelCoords &modelCoords) const
Initialize given the boundaries of the graph coordinates. The output is useful for the Checker class...
void setStopX(double stopX)
Set method for x grid line upper bound (inclusive).
int computeCount(bool linearAxis, double start, double stop, double step) const
Compute axis scale count from the other axis parameters.
CoordScale coordScaleXTheta() const
Get method for linear/log scale on x/theta.
void setDisableX(GridCoordDisable disableX)
Set method for x grid line disabled variable.
void setStopY(double yStop)
Set method for y grid line upper bound (inclusive).
Model for DlgSettingsCoords and CmdSettingsCoords.
void setDisableY(GridCoordDisable disableY)
Set method for y grid line disabled variable.
void setCountX(unsigned int countX)
Set method for x grid line count.
CoordScale coordScaleYRadius() const
Get method for linear/log scale on y/radius.
void setStartY(double yStart)
Set method for y grid line lower bound (inclusive).
CoordsType coordsType() const
Get method for coordinates type.
void transformScreenToRawGraph(const QPointF &coordScreen, QPointF &coordGraph) const
Transform from cartesian pixel screen coordinates to cartesian/polar graph coordinates.
double stepY() const
Get method for y grid line increment.
double computeStop(bool linearAxis, double start, double step, int count) const
Compute axis scale stop from the other axis parameters.
double originRadius() const
Get method for origin radius in polar mode.
double computeStep(bool linearAxis, double start, double stop, int count) const
Compute axis scale step from the other axis parameters.