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 QPointF &boundingRectGraphMax,
181  const DocumentModelCoords &modelCoords) const
182 {
183  LOG4CPP_INFO_S ((*mainCat)) << "GridInitializer::initializeWithNarrowCoverage";
184 
185  DocumentModelGridDisplay modelGridDisplay;
186 
187  int count;
188  double start, stop, step;
189 
190  // X/theta coordinate
191  axisScale (boundingRectGraphMin.x(),
192  boundingRectGraphMax.x(),
193  (modelCoords.coordScaleXTheta() == COORD_SCALE_LINEAR),
194  start,
195  stop,
196  step,
197  count);
198 
199  modelGridDisplay.setDisableX (GRID_COORD_DISABLE_COUNT);
200  modelGridDisplay.setCountX (count);
201  modelGridDisplay.setStartX (start);
202  modelGridDisplay.setStepX (step);
203  modelGridDisplay.setStopX (stop);
204 
205  // Y/radius coordinate
206  axisScale (boundingRectGraphMin.y(),
207  boundingRectGraphMax.y(),
208  (modelCoords.coordScaleYRadius() == COORD_SCALE_LINEAR),
209  start,
210  stop,
211  step,
212  count);
213 
214  modelGridDisplay.setDisableY (GRID_COORD_DISABLE_COUNT);
215  modelGridDisplay.setCountY (count);
216  modelGridDisplay.setStartY (start);
217  modelGridDisplay.setStepY (step);
218  modelGridDisplay.setStopY (stop);
219 
220  modelGridDisplay.setStable (true);
221 
222  return modelGridDisplay;
223 }
224 
226  const QPointF &boundingRectGraphMax,
227  const DocumentModelCoords &modelCoords,
228  const Transformation &transformation,
229  const QSize &imageSize) const
230 {
231  LOG4CPP_INFO_S ((*mainCat)) << "GridInitializer::initializeWithWidePolarCoverage";
232 
233  DocumentModelGridDisplay modelGridDisplay = initializeWithNarrowCoverage (boundingRectGraphMin,
234  boundingRectGraphMax,
235  modelCoords);
236 
237  if (modelCoords.coordsType() == COORDS_TYPE_POLAR) {
238 
239  overridePolarCoordinateSettings (modelCoords,
240  transformation,
241  modelGridDisplay,
242  imageSize);
243  }
244 
245  return modelGridDisplay;
246 }
247 
248 void GridInitializer::overridePolarCoordinateSettings (const DocumentModelCoords &modelCoords,
249  const Transformation &transformation,
250  DocumentModelGridDisplay &modelGridDisplay,
251  const QSize &imageSize) const
252 {
253  ENGAUGE_ASSERT (modelCoords.coordsType() == COORDS_TYPE_POLAR);
254 
255  // We make sure the angular range is over the entire circle, which is probably useful
256  // unless the orgin is very close to a corner of the graph, in which case the large range does not hurt anything
257  double startX = 0.0;
258  double stopX = 360.0;
259  double stepX = 30.0;
260  int countX = (int) (0.5 + (stopX - startX) / stepX);
261  modelGridDisplay.setStartX (startX);
262  modelGridDisplay.setStepX (stepX);
263  modelGridDisplay.setStopX (stopX);
264  modelGridDisplay.setCountX (countX);
265 
266  // We extend the range to cover the four corners of the image, since otherwise
267  // areas around at least some graph corners are not covered by the grid lines
268  QPointF posTL, posBL, posTR, posBR;
269  transformation.transformScreenToRawGraph (QPointF (0 , imageSize.height ()), posTL);
270  transformation.transformScreenToRawGraph (QPointF (0 , 0 ), posBL);
271  transformation.transformScreenToRawGraph (QPointF (imageSize.width (), imageSize.height ()), posTR);
272  transformation.transformScreenToRawGraph (QPointF (imageSize.width (), 0 ), posBR);
273 
274  double radiusTL = qSqrt (posTL.x () * posTL.x () + posTL.y () * posTL.y ());
275  double radiusBL = qSqrt (posBL.x () * posBL.x () + posBL.y () * posBL.y ());
276  double radiusTR = qSqrt (posTR.x () * posTR.x () + posTR.y () * posTR.y ());
277  double radiusBR = qSqrt (posBR.x () * posBR.x () + posBR.y () * posBR.y ());
278 
279  double radius = qMax (qMax (qMax (radiusTL, radiusBL), radiusTR), radiusBR);
280 
281  double startY = (modelCoords.coordScaleYRadius() == COORD_SCALE_LINEAR ?
282  0.0 :
283  modelCoords.originRadius());
284  double stopY = radius;
285  double stepY = modelGridDisplay.stepY ();
286  double denominator = (modelCoords.coordScaleYRadius() == COORD_SCALE_LINEAR ?
287  stepY :
288  qLn (stepY));
289  int countY = 1;
290  if (denominator != 0) {
291  countY = (modelCoords.coordScaleYRadius() == COORD_SCALE_LINEAR ?
292  (int) (0.5 + (stopY - startY) / denominator) :
293  (int) (0.5 + (qLn (stopY) - qLn (startY)) / denominator));
294  }
295 
296  modelGridDisplay.setStartY (startY);
297  modelGridDisplay.setStopY (stopY);
298  modelGridDisplay.setCountY (countY);
299 }
300 
301 double GridInitializer::roundOffToPower(double arg,
302  int power) const
303 {
304  double powerOf10 = pow ((double) 10, power);
305  return powerOf10 * floor (arg / powerOf10 + 0.5);
306 }
307 
308 int GridInitializer::valuePower(double value) const
309 {
310  const int minPower = -30; // MAX_DOUBLE is 10^38
311 
312  double avalue = fabs(value);
313  if (avalue < pow(10.0, minPower)) {
314  return minPower;
315  } else {
316  return (int) floor (log10 (avalue));
317  }
318 }
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.
GridInitializer()
Single constructor.
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.
DocumentModelGridDisplay initializeWithNarrowCoverage(const QPointF &boundingRectGraphMin, const QPointF &boundingRectGraphMax, const DocumentModelCoords &modelCoords) const
Initialize given the boundaries of the graph coordinates.
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.
DocumentModelGridDisplay initializeWithWidePolarCoverage(const QPointF &boundingRectGraphMin, const QPointF &boundingRectGraphMax, 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...
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.