Engauge Digitizer  2
ExportXThetaValuesMergedFunctions.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 "ExportXThetaValuesMergedFunctions.h"
8 #include "ExportAlignLinear.h"
9 #include "ExportAlignLog.h"
10 #include "ExportLayoutFunctions.h"
11 #include "ExportPointsSelectionFunctions.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
75  if (m_modelExport.pointsIntervalUnitsFunctions() == EXPORT_POINTS_INTERVAL_UNITS_GRAPH) {
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  const double ARBITRARY_Y = 0.0;
125 
126  // Screen coordinates of endpoints
127  QPointF posScreenFirst, posScreenLast;
128  m_transformation.transformRawGraphToScreen(QPointF (xThetaMin,
129  ARBITRARY_Y),
130  posScreenFirst);
131  m_transformation.transformRawGraphToScreen(QPointF (xThetaMax,
132  ARBITRARY_Y),
133  posScreenLast);
134  double deltaScreenX = posScreenLast.x() - posScreenFirst.x();
135  double deltaScreenY = posScreenLast.y() - posScreenFirst.y();
136  double deltaScreen = qSqrt (deltaScreenX * deltaScreenX + deltaScreenY * deltaScreenY);
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 < deltaScreen)) {
143  s = interval / deltaScreen;
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
175  if (m_modelExport.pointsIntervalUnitsFunctions() == EXPORT_POINTS_INTERVAL_UNITS_GRAPH) {
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  while (xTheta > xThetaMin) {
195  xTheta /= m_modelExport.pointsIntervalFunctions(); // Go backwards until reaching or passing minimum
196  }
197  if (xTheta < xThetaMin) {
198  values [xThetaMin] = true; // We passed minimum so insert point right at xThetaMin
199  }
200 
201  xTheta *= m_modelExport.pointsIntervalFunctions();
202  while (xTheta <= xThetaMax) {
203  values [xTheta] = true;
204  xTheta *= m_modelExport.pointsIntervalFunctions(); // Insert point at a simple number
205  }
206 
207  if (xTheta > xThetaMax) {
208  values [xThetaMax] = true; // We passed maximum so insert point right at xThetaMax
209  }
210 
211  return values.keys();
212 }
213 
214 ExportValuesXOrY ExportXThetaValuesMergedFunctions::periodicLogScreen (double xThetaMin,
215  double xThetaMax) const
216 {
217  LOG4CPP_INFO_S ((*mainCat)) << "ExportXThetaValuesMergedFunctions::periodicLogScreen";
218 
219  const double ARBITRARY_Y = 0.0;
220 
221  // Screen coordinates of endpoints
222  QPointF posScreenFirst, posScreenLast;
223  m_transformation.transformRawGraphToScreen(QPointF (xThetaMin,
224  ARBITRARY_Y),
225  posScreenFirst);
226  m_transformation.transformRawGraphToScreen(QPointF (xThetaMax,
227  ARBITRARY_Y),
228  posScreenLast);
229  double deltaScreenX = posScreenLast.x() - posScreenFirst.x();
230  double deltaScreenY = posScreenLast.y() - posScreenFirst.y();
231  double deltaScreen = qSqrt (deltaScreenX * deltaScreenX + deltaScreenY * deltaScreenY);
232 
233  // Need calculations to find the scaling to be applied to successive points
234  double s = 1.0;
235  double interval = m_modelExport.pointsIntervalFunctions();
236  if ((interval > 0) &&
237  (interval < deltaScreen)) {
238  s = interval / deltaScreen;
239  }
240 
241  // Example: xThetaMin=0.1 and xThetaMax=100 (points are 0.1, 1, 10, 100) with s=1/3 so scale should be 10
242  // which multiples 0.1 to get 1. This uses s=(log(xNext)-log(xMin))/(log(xMax)-log(xMin))
243  double xNext = qExp (qLn (xThetaMin) + s * (qLn (xThetaMax) - qLn (xThetaMin)));
244  double scale = xNext / xThetaMin;
245 
246  ValuesVectorXOrY values;
247 
248  double xTheta = xThetaMin;
249  while (xTheta <= xThetaMax) {
250 
251  values [xTheta] = true;
252 
253  xTheta *= scale;
254  }
255 
256  return values.keys();
257 }
258 
260 {
261  LOG4CPP_INFO_S ((*mainCat)) << "ExportXThetaValuesMergedFunctions::xThetaValues";
262 
263  if (m_modelExport.pointsSelectionFunctions() == EXPORT_POINTS_SELECTION_FUNCTIONS_INTERPOLATE_PERIODIC) {
264 
265  // Special case that occurs when there are no points
266  if (m_modelExport.pointsIntervalFunctions() == 0) {
267 
268  ExportValuesXOrY empty;
269  return empty;
270 
271  } else {
272 
273  bool isLinear = (m_transformation.modelCoords().coordScaleXTheta() == COORD_SCALE_LINEAR);
274  if (isLinear) {
275  return periodicLinear ();
276  } else {
277  return periodicLog ();
278  }
279  }
280  } else {
281 
282  // Return the gathered values
283  return m_xThetaValuesRaw.keys();
284 
285  }
286 }
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...
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...
double firstSimplestNumber() const
Result.
CoordScale coordScaleXTheta() const
Get method for linear/log scale on x/theta.
ExportPointsIntervalUnits pointsIntervalUnitsFunctions() const
Get method for points interval units for functions.
double firstSimplestNumber() const
Result.
ExportPointsSelectionFunctions pointsSelectionFunctions() const
Get method for point selection for functions.