Engauge Digitizer  2
 All Classes Functions Variables Typedefs Enumerations Friends Pages
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 "ExportAlignLinear.h"
8 #include "ExportAlignLog.h"
9 #include "ExportLayoutFunctions.h"
10 #include "ExportPointsSelectionFunctions.h"
11 #include "ExportXThetaValuesMergedFunctions.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  // 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
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 (qAbs (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 }
ExportPointsSelectionFunctions pointsSelectionFunctions() const
Get method for point selection for functions.
ExportXThetaValuesMergedFunctions(const DocumentModelExportFormat &modelExport, const ValuesVectorXOrY &xThetaValuesRaw, const Transformation &transformation)
Single constructor.
ExportPointsIntervalUnits pointsIntervalUnitsFunctions() const
Get method for points interval units for functions.
Model for DlgSettingsExportFormat and CmdSettingsExportFormat.
ExportValuesXOrY xThetaValues() const
Resulting x/theta values for all included functions.
Pick first simplest x value between specified min and max, for linear scaling.
double pointsIntervalFunctions() const
Get method for points interval for functions.
Pick first simplest x value between specified min and max, for log scaling.
DocumentModelCoords modelCoords() const
Get method for DocumentModelCoords.
Affine transformation between screen and graph coordinates, based on digitized axis points...
CoordScale coordScaleXTheta() const
Get method for linear/log scale on x/theta.
void transformRawGraphToScreen(const QPointF &pointRaw, QPointF &pointScreen) const
Transform from raw graph coordinates to linear cartesian graph coordinates, then to screen coordinate...