Engauge Digitizer  2
ExportXThetaValuesMergedFunctions.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 "ExportAlignLinear.h"
8 #include "ExportAlignLog.h"
12 #include "Logger.h"
13 #include "Point.h"
14 #include <qmath.h>
15 #include "Transformation.h"
16 
17 using namespace std;
18 
20  const ValuesVectorXOrY &xThetaValuesRaw,
21  const Transformation &transformation) :
22  m_modelExport (modelExport),
23  m_xThetaValuesRaw (xThetaValuesRaw),
24  m_transformation (transformation)
25 {
26 }
27 
28 void ExportXThetaValuesMergedFunctions::firstSimplestNumberLinear (double &xThetaFirstSimplestNumber,
29  double &xThetaMin,
30  double &xThetaMax) const
31 {
32  LOG4CPP_INFO_S ((*mainCat)) << "ExportXThetaValuesMergedFunctions::firstSimplestNumberLinear";
33 
34  // X/theta range
35  xThetaMin = m_xThetaValuesRaw.firstKey();
36  xThetaMax = m_xThetaValuesRaw.lastKey();
37 
38  // Compute offset that gives the simplest numbers
39  ExportAlignLinear alignLinear (xThetaMin,
40  xThetaMax);
41 
42  xThetaFirstSimplestNumber = alignLinear.firstSimplestNumber ();
43 }
44 
45 void ExportXThetaValuesMergedFunctions::firstSimplestNumberLog (double &xThetaFirstSimplestNumber,
46  double &xThetaMin,
47  double &xThetaMax) const
48 {
49  LOG4CPP_INFO_S ((*mainCat)) << "ExportXThetaValuesMergedFunctions::firstSimplestNumberLog";
50 
51  // X/theta range
52  xThetaMin = m_xThetaValuesRaw.firstKey();
53  xThetaMax = m_xThetaValuesRaw.lastKey();
54 
55  // Compute offset that gives the simplest numbers
56  ExportAlignLog alignLog (xThetaMin,
57  xThetaMax);
58 
59  xThetaFirstSimplestNumber = alignLog.firstSimplestNumber();
60 }
61 
62 ExportValuesXOrY ExportXThetaValuesMergedFunctions::periodicLinear() const
63 {
64  LOG4CPP_INFO_S ((*mainCat)) << "ExportXThetaValuesMergedFunctions::periodicLinear";
65 
66  if (m_xThetaValuesRaw.count () > 0) {
67 
68  double xThetaFirstSimplestNumber, xThetaMin, xThetaMax;
69  firstSimplestNumberLinear (xThetaFirstSimplestNumber,
70  xThetaMin,
71  xThetaMax);
72 
73  // Assuming user picks an appropriate interval increment, numbering starting at xThetaFirstSimplestNumber
74  // will give nice x/theta numbers
76  return periodicLinearGraph(xThetaFirstSimplestNumber,
77  xThetaMin,
78  xThetaMax);
79  } else {
80  return periodicLinearScreen(xThetaMin,
81  xThetaMax);
82  }
83  } else {
84 
85  ExportValuesXOrY emptyList;
86  return emptyList;
87  }
88 }
89 
90 ExportValuesXOrY ExportXThetaValuesMergedFunctions::periodicLinearGraph(double xThetaFirstSimplestNumber,
91  double xThetaMin,
92  double xThetaMax) const
93 {
94  LOG4CPP_INFO_S ((*mainCat)) << "ExportXThetaValuesMergedFunctions::periodicLinearGraph";
95 
96  // Convert the gathered values into a periodic sequence
97  ValuesVectorXOrY values;
98  double xTheta = xThetaFirstSimplestNumber;
99  while (xTheta > xThetaMin) {
100  xTheta -= m_modelExport.pointsIntervalFunctions(); // Go backwards until reaching or passing minimum
101  }
102  if (xTheta < xThetaMin) {
103  values [xThetaMin] = true; // We passed minimum so insert point right at xThetaMin
104  }
105 
106  xTheta += m_modelExport.pointsIntervalFunctions();
107  while (xTheta <= xThetaMax) {
108  values [xTheta] = true;
109  xTheta += m_modelExport.pointsIntervalFunctions(); // Insert point at a simple number
110  }
111 
112  if (xTheta > xThetaMax) {
113  values [xThetaMax] = true; // We passed maximum so insert point right at xThetaMax
114  }
115 
116  return values.keys();
117 }
118 
119 ExportValuesXOrY ExportXThetaValuesMergedFunctions::periodicLinearScreen (double xThetaMin,
120  double xThetaMax) const
121 {
122  LOG4CPP_INFO_S ((*mainCat)) << "ExportXThetaValuesMergedFunctions::periodicLinearScreen";
123 
124  // This must be greater than zero. Otherwise, logarithmic y axis will trigger errors in the
125  // transform, which cascades into NaN values for the x coordinates below
126  const double ARBITRARY_Y = 1.0;
127 
128  // Screen coordinates of endpoints
129  QPointF posScreenFirst, posScreenLast;
130  m_transformation.transformRawGraphToScreen(QPointF (xThetaMin,
131  ARBITRARY_Y),
132  posScreenFirst);
133  m_transformation.transformRawGraphToScreen(QPointF (xThetaMax,
134  ARBITRARY_Y),
135  posScreenLast);
136  double deltaScreenX = posScreenLast.x() - posScreenFirst.x();
137 
138  // Need calculations to find the scaling to be applied to successive points
139  double s = 1.0;
140  double interval = m_modelExport.pointsIntervalFunctions();
141  if ((interval > 0) &&
142  (interval < deltaScreenX)) {
143  s = interval / deltaScreenX;
144  }
145 
146  // Example: xThetaMin=0.1 and xThetaMax=100 (points are 0.1, 1, 10, 100) with s=1/3 so scale should be 10
147  // which multiples 0.1 to get 1. This uses s=(log(xNext)-log(xMin))/(log(xMax)-log(xMin))
148  double xNext = xThetaMin + s * (xThetaMax - xThetaMin);
149  double delta = xNext - xThetaMin;
150 
151  ValuesVectorXOrY values;
152 
153  double xTheta = xThetaMin;
154  while (xTheta <= xThetaMax) {
155 
156  values [xTheta] = true;
157 
158  xTheta += delta;
159  }
160 
161  return values.keys();
162 }
163 
164 ExportValuesXOrY ExportXThetaValuesMergedFunctions::periodicLog() const
165 {
166  LOG4CPP_INFO_S ((*mainCat)) << "ExportXThetaValuesMergedFunctions::periodicLog";
167 
168  double xThetaFirstSimplestNumber, xThetaMin, xThetaMax;
169  firstSimplestNumberLog (xThetaFirstSimplestNumber,
170  xThetaMin,
171  xThetaMax);
172 
173  // Assuming user picks an appropriate interval increment, numbering starting at xThetaFirstSimplestNumber
174  // will give nice x/theta numbers
176  return periodicLogGraph(xThetaFirstSimplestNumber,
177  xThetaMin,
178  xThetaMax);
179  } else {
180  return periodicLogScreen(xThetaMin,
181  xThetaMax);
182  }
183 }
184 
185 ExportValuesXOrY ExportXThetaValuesMergedFunctions::periodicLogGraph (double xThetaFirstSimplestNumber,
186  double xThetaMin,
187  double xThetaMax) const
188 {
189  LOG4CPP_INFO_S ((*mainCat)) << "ExportXThetaValuesMergedFunctions::periodicLogGraph";
190 
191  // Convert the gathered values into a periodic sequence
192  ValuesVectorXOrY values;
193  double xTheta = xThetaFirstSimplestNumber;
194  if (m_modelExport.pointsIntervalFunctions() > 1) { // Safe to iterate
195  while (xTheta > xThetaMin) {
196  xTheta /= m_modelExport.pointsIntervalFunctions(); // Go backwards until reaching or passing minimum
197  }
198  }
199  if (xTheta < xThetaMin) {
200  values [xThetaMin] = true; // We passed minimum so insert point right at xThetaMin
201  }
202 
203  if (m_modelExport.pointsIntervalFunctions() > 1) { // Safe to iterate
204  xTheta *= m_modelExport.pointsIntervalFunctions();
205  while (xTheta <= xThetaMax) {
206  values [xTheta] = true;
207  xTheta *= m_modelExport.pointsIntervalFunctions(); // Insert point at a simple number
208  }
209  }
210 
211  if (xTheta > xThetaMax) {
212  values [xThetaMax] = true; // We passed maximum so insert point right at xThetaMax
213  }
214 
215  return values.keys();
216 }
217 
218 ExportValuesXOrY ExportXThetaValuesMergedFunctions::periodicLogScreen (double xThetaMin,
219  double xThetaMax) const
220 {
221  LOG4CPP_INFO_S ((*mainCat)) << "ExportXThetaValuesMergedFunctions::periodicLogScreen";
222 
223  const double ARBITRARY_Y = 0.0;
224 
225  // Screen coordinates of endpoints
226  QPointF posScreenFirst, posScreenLast;
227  m_transformation.transformRawGraphToScreen(QPointF (xThetaMin,
228  ARBITRARY_Y),
229  posScreenFirst);
230  m_transformation.transformRawGraphToScreen(QPointF (xThetaMax,
231  ARBITRARY_Y),
232  posScreenLast);
233  double deltaScreenX = posScreenLast.x() - posScreenFirst.x();
234  double deltaScreenY = posScreenLast.y() - posScreenFirst.y();
235  double deltaScreen = qSqrt (deltaScreenX * deltaScreenX + deltaScreenY * deltaScreenY);
236 
237  // Need calculations to find the scaling to be applied to successive points
238  double s = 1.0;
239  double interval = m_modelExport.pointsIntervalFunctions();
240  if ((interval > 0) &&
241  (interval < deltaScreen)) {
242  s = interval / deltaScreen;
243  }
244 
245  // Example: xThetaMin=0.1 and xThetaMax=100 (points are 0.1, 1, 10, 100) with s=1/3 so scale should be 10
246  // which multiples 0.1 to get 1. This uses s=(log(xNext)-log(xMin))/(log(xMax)-log(xMin))
247  double xNext = qExp (qLn (xThetaMin) + s * (qLn (xThetaMax) - qLn (xThetaMin)));
248  double scale = xNext / xThetaMin;
249 
250  ValuesVectorXOrY values;
251 
252  double xTheta = xThetaMin;
253  while (xTheta <= xThetaMax) {
254 
255  values [xTheta] = true;
256 
257  xTheta *= scale;
258  }
259 
260  return values.keys();
261 }
262 
264 {
265  LOG4CPP_INFO_S ((*mainCat)) << "ExportXThetaValuesMergedFunctions::xThetaValues";
266 
268 
269  // Special case that occurs when there are no points
270  if (qAbs (m_modelExport.pointsIntervalFunctions()) <= 0) {
271 
272  ExportValuesXOrY empty;
273  return empty;
274 
275  } else {
276 
277  bool isLinear = (m_transformation.modelCoords().coordScaleXTheta() == COORD_SCALE_LINEAR);
278  if (isLinear) {
279  return periodicLinear ();
280  } else {
281  return periodicLog ();
282  }
283  }
284  } else {
285 
286  // Return the gathered values
287  return m_xThetaValuesRaw.keys();
288 
289  }
290 }
ExportValuesXOrY xThetaValues() const
Resulting x/theta values for all included functions.
ExportXThetaValuesMergedFunctions(const DocumentModelExportFormat &modelExport, const ValuesVectorXOrY &xThetaValuesRaw, const Transformation &transformation)
Single constructor.
Model for DlgSettingsExportFormat and CmdSettingsExportFormat.
Pick first simplest x value between specified min and max, for linear scaling.
void transformRawGraphToScreen(const QPointF &pointRaw, QPointF &pointScreen) const
Transform from raw graph coordinates to linear cartesian graph coordinates, then to screen coordinate...
#define LOG4CPP_INFO_S(logger)
Definition: convenience.h:18
double pointsIntervalFunctions() const
Get method for points interval for functions.
DocumentModelCoords modelCoords() const
Get method for DocumentModelCoords.
Pick first simplest x value between specified min and max, for log scaling.
Affine transformation between screen and graph coordinates, based on digitized axis points...
QList< double > ExportValuesXOrY
CoordScale coordScaleXTheta() const
Get method for linear/log scale on x/theta.
ExportPointsIntervalUnits pointsIntervalUnitsFunctions() const
Get method for points interval units for functions.
log4cpp::Category * mainCat
Definition: Logger.cpp:14
ExportPointsSelectionFunctions pointsSelectionFunctions() const
Get method for point selection for functions.
QMap< double, bool > ValuesVectorXOrY