Engauge Digitizer 2
Loading...
Searching...
No Matches
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
17using 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
28void 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
45void 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
62ExportValuesXOrY 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
90ExportValuesXOrY 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
119ExportValuesXOrY 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
164ExportValuesXOrY 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
185ExportValuesXOrY 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
214ExportValuesXOrY 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}
CoordScale coordScaleXTheta() const
Get method for linear/log scale on x/theta.
Model for DlgSettingsExportFormat and CmdSettingsExportFormat.
ExportPointsSelectionFunctions pointsSelectionFunctions() const
Get method for point selection for functions.
double pointsIntervalFunctions() const
Get method for points interval for functions.
ExportPointsIntervalUnits pointsIntervalUnitsFunctions() const
Get method for points interval units for functions.
Pick first simplest x value between specified min and max, for linear scaling.
Pick first simplest x value between specified min and max, for log scaling.
ExportValuesXOrY xThetaValues() const
Resulting x/theta values for all included functions.
ExportXThetaValuesMergedFunctions(const DocumentModelExportFormat &modelExport, const ValuesVectorXOrY &xThetaValuesRaw, const Transformation &transformation)
Single constructor.
Affine transformation between screen and graph coordinates, based on digitized axis points.
void transformRawGraphToScreen(const QPointF &pointRaw, QPointF &pointScreen) const
Transform from raw graph coordinates to linear cartesian graph coordinates, then to screen coordinate...
DocumentModelCoords modelCoords() const
Get method for DocumentModelCoords.