Engauge Digitizer  2
ExportFileFunctions.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 "CallbackGatherXThetasInCurves.h"
8 #include "CallbackGatherXThetasInGridLines.h"
9 #include "CurveConnectAs.h"
10 #include "Document.h"
11 #include "DocumentModelGeneral.h"
12 #include "EngaugeAssert.h"
13 #include "ExportFileFunctions.h"
14 #include "ExportLayoutFunctions.h"
15 #include "ExportOrdinalsSmooth.h"
16 #include "ExportXThetaValuesMergedFunctions.h"
17 #include "FormatCoordsUnits.h"
18 #include "GridLineLimiter.h"
19 #include "LinearToLog.h"
20 #include "Logger.h"
21 #include <qmath.h>
22 #include <QTextStream>
23 #include <QVector>
24 #include "Spline.h"
25 #include "SplinePair.h"
26 #include "Transformation.h"
27 #include <vector>
28 
29 using namespace std;
30 
32 {
33 }
34 
35 void ExportFileFunctions::exportAllPerLineXThetaValuesMerged (const DocumentModelExportFormat &modelExportOverride,
36  const Document &document,
37  const MainWindowModel &modelMainWindow,
38  const QStringList &curvesIncluded,
39  const ExportValuesXOrY &xThetaValues,
40  const QString &delimiter,
41  const Transformation &transformation,
42  bool isLogXTheta,
43  bool isLogYRadius,
44  const CurveLimits curveLimitsMin,
45  const CurveLimits curveLimitsMax,
46  QTextStream &str,
47  unsigned int &numWritesSoFar) const
48 {
49  LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::exportAllPerLineXThetaValuesMerged";
50 
51  int curveCount = curvesIncluded.count();
52  int xThetaCount = xThetaValues.count();
53  QVector<QVector<QString*> > yRadiusValues (curveCount, QVector<QString*> (xThetaCount));
54  initializeYRadiusValues (curvesIncluded,
55  xThetaValues,
56  yRadiusValues);
57  loadYRadiusValues (modelExportOverride,
58  document,
59  modelMainWindow,
60  curvesIncluded,
61  transformation,
62  isLogXTheta,
63  isLogYRadius,
64  xThetaValues,
65  curveLimitsMin,
66  curveLimitsMax,
67  yRadiusValues);
68 
69  outputXThetaYRadiusValues (modelExportOverride,
70  document.modelCoords(),
71  document.modelGeneral(),
72  modelMainWindow,
73  curvesIncluded,
74  xThetaValues,
75  transformation,
76  yRadiusValues,
77  delimiter,
78  str,
79  numWritesSoFar);
80  destroy2DArray (yRadiusValues);
81 }
82 
83 void ExportFileFunctions::exportOnePerLineXThetaValuesMerged (const DocumentModelExportFormat &modelExportOverride,
84  const Document &document,
85  const MainWindowModel &modelMainWindow,
86  const QStringList &curvesIncluded,
87  const ExportValuesXOrY &xThetaValues,
88  const QString &delimiter,
89  const Transformation &transformation,
90  bool isLogXTheta,
91  bool isLogYRadius,
92  const CurveLimits curveLimitsMin,
93  const CurveLimits curveLimitsMax,
94  QTextStream &str,
95  unsigned int &numWritesSoFar) const
96 {
97  LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::exportOnePerLineXThetaValuesMerged";
98 
99  QStringList::const_iterator itr;
100  for (itr = curvesIncluded.begin(); itr != curvesIncluded.end(); itr++) {
101 
102  // This curve
103  const int CURVE_COUNT = 1;
104  QString curveIncluded = *itr;
105  QStringList curvesIncludedIter (curveIncluded);
106 
107  int xThetaCount = xThetaValues.count();
108  QVector<QVector<QString*> > yRadiusValues (CURVE_COUNT, QVector<QString*> (xThetaCount));
109  initializeYRadiusValues (curvesIncludedIter,
110  xThetaValues,
111  yRadiusValues);
112  loadYRadiusValues (modelExportOverride,
113  document,
114  modelMainWindow,
115  curvesIncludedIter,
116  transformation,
117  isLogXTheta,
118  isLogYRadius,
119  xThetaValues,
120  curveLimitsMin,
121  curveLimitsMax,
122  yRadiusValues);
123  outputXThetaYRadiusValues (modelExportOverride,
124  document.modelCoords(),
125  document.modelGeneral(),
126  modelMainWindow,
127  curvesIncludedIter,
128  xThetaValues,
129  transformation,
130  yRadiusValues,
131  delimiter,
132  str,
133  numWritesSoFar);
134  destroy2DArray (yRadiusValues);
135  }
136 }
137 
139  const Document &document,
140  const MainWindowModel &modelMainWindow,
141  const Transformation &transformation,
142  QTextStream &str,
143  unsigned int &numWritesSoFar) const
144 {
145  LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::exportToFile";
146 
147  // Log coordinates must be temporarily transformed to linear coordinates
148  bool isLogXTheta = (document.modelCoords().coordScaleXTheta() == COORD_SCALE_LOG);
149  bool isLogYRadius = (document.modelCoords().coordScaleYRadius() == COORD_SCALE_LOG);
150 
151  // Identify curves to be included
152  QStringList curvesIncluded = curvesToInclude (modelExportOverride,
153  document,
154  document.curvesGraphsNames(),
155  CONNECT_AS_FUNCTION_SMOOTH,
156  CONNECT_AS_FUNCTION_STRAIGHT);
157 
158  // Delimiter
159  const QString delimiter = exportDelimiterToText (modelExportOverride.delimiter(),
160  modelExportOverride.header() == EXPORT_HEADER_GNUPLOT);
161 
162  // Get x/theta values to be used. Also get the endpoint limits, if any
163  CurveLimits curveLimitsMin, curveLimitsMax;
164  ValuesVectorXOrY valuesVector;
165  if (modelExportOverride.pointsSelectionFunctions() == EXPORT_POINTS_SELECTION_FUNCTIONS_INTERPOLATE_GRID_LINES) {
166  CallbackGatherXThetasInGridLines ftor (modelMainWindow,
167  modelExportOverride,
168  curvesIncluded,
169  transformation,
170  document);
171  Functor2wRet<const QString &, const Point &, CallbackSearchReturn> ftorWithCallback = functor_ret (ftor,
173  document.iterateThroughCurvesPointsGraphs(ftorWithCallback);
174  valuesVector = ftor.xThetaValuesRaw();
175  curveLimitsMin = ftor.curveLimitsMin();
176  curveLimitsMax = ftor.curveLimitsMax();
177  } else {
178  CallbackGatherXThetasInCurves ftor (modelExportOverride,
179  curvesIncluded,
180  transformation);
181  Functor2wRet<const QString &, const Point &, CallbackSearchReturn> ftorWithCallback = functor_ret (ftor,
183  document.iterateThroughCurvesPointsGraphs(ftorWithCallback);
184  valuesVector = ftor.xThetaValuesRaw();
185  curveLimitsMin = ftor.curveLimitsMin();
186  curveLimitsMax = ftor.curveLimitsMax();
187  }
188 
189  ExportXThetaValuesMergedFunctions exportXTheta (modelExportOverride,
190  valuesVector,
191  transformation);
192 
193  ExportValuesXOrY xThetaValuesMerged = exportXTheta.xThetaValues ();
194 
195  // Skip if every curve was a relation
196  if (xThetaValuesMerged.count() > 0) {
197 
198  // Export in one of two layouts
199  if (modelExportOverride.layoutFunctions() == EXPORT_LAYOUT_ALL_PER_LINE) {
200  exportAllPerLineXThetaValuesMerged (modelExportOverride,
201  document,
202  modelMainWindow,
203  curvesIncluded,
204  xThetaValuesMerged,
205  delimiter,
206  transformation,
207  isLogXTheta,
208  isLogYRadius,
209  curveLimitsMin,
210  curveLimitsMax,
211  str,
212  numWritesSoFar);
213  } else {
214  exportOnePerLineXThetaValuesMerged (modelExportOverride,
215  document,
216  modelMainWindow,
217  curvesIncluded,
218  xThetaValuesMerged,
219  delimiter,
220  transformation,
221  isLogXTheta,
222  isLogYRadius,
223  curveLimitsMin,
224  curveLimitsMax,
225  str,
226  numWritesSoFar);
227  }
228  }
229 }
230 
231 void ExportFileFunctions::initializeYRadiusValues (const QStringList &curvesIncluded,
232  const ExportValuesXOrY &xThetaValuesMerged,
233  QVector<QVector<QString*> > &yRadiusValues) const
234 {
235  LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::initializeYRadiusValues";
236 
237  // Initialize every entry with empty string
238  int curveCount = curvesIncluded.count();
239  int xThetaCount = xThetaValuesMerged.count();
240  for (int row = 0; row < xThetaCount; row++) {
241  for (int col = 0; col < curveCount; col++) {
242  yRadiusValues [col] [row] = new QString;
243  }
244  }
245 }
246 
247 double ExportFileFunctions::linearlyInterpolate (const Points &points,
248  double xThetaValue,
249  const Transformation &transformation) const
250 {
251  // LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::linearlyInterpolate";
252 
253  // If point is within the range of the function points then interpolation will be used, otherwise
254  // extrapolation will be used
255  double yRadius = 0;
256  QPointF posGraphBefore; // Not set until ip=1
257  bool foundIt = false;
258  for (int ip = 0; !foundIt && (ip < points.count()); ip++) {
259 
260  const Point &point = points.at (ip);
261  QPointF posGraph;
262  transformation.transformScreenToRawGraph (point.posScreen(),
263  posGraph);
264 
265  // Cases where we have found it at this point in the code
266  // (1) interpolation case where (xBefore < xThetaValue < xAfter)
267  // (2) extrapolation case where (xThetaValue < xBefore < xAfter and ip=0) for which we delay finding it until ip=1 so we have
268  // two points for extrapolating. This case is why we have the subtle test for ip>0 in the next line
269  if (xThetaValue <= posGraph.x() && (ip > 0)) {
270 
271  foundIt = true;
272 
273  // Case 1 comments: xThetaValue is between posGraphBefore and posGraph. Note that if posGraph.x()=posGraphBefore.x() then
274  // previous iteration of loop would have been used for interpolation, and then the loop was exited. Range of s is 0<s<1
275  // Case 2 comments: Range of s is s<0
276  yRadius = linearlyInterpolateYRadiusFromTwoPoints (xThetaValue,
277  transformation.modelCoords(),
278  posGraphBefore,
279  posGraph);
280 
281  break;
282  }
283 
284  posGraphBefore = posGraph;
285  }
286 
287  if (!foundIt) {
288 
289  if (points.count() > 1) {
290 
291  // Extrapolation will be used since point is out of the range of the function points. Specifically, it is greater than the
292  // last x value in the function. Range of s is 1<s
293  int N = points.count();
294  const Point &pointLast = points.at (N - 1);
295  const Point &pointBefore = points.at (N - 2);
296  QPointF posGraphLast;
297  transformation.transformScreenToRawGraph (pointLast.posScreen(),
298  posGraphLast);
299  transformation.transformScreenToRawGraph (pointBefore.posScreen(),
300  posGraphBefore);
301  yRadius = linearlyInterpolateYRadiusFromTwoPoints (xThetaValue,
302  transformation.modelCoords(),
303  posGraphBefore,
304  posGraphLast);
305 
306  } else if (points.count() == 1) {
307 
308  // Just use the single point
309  yRadius = posGraphBefore.y();
310 
311  } else {
312 
313  ENGAUGE_ASSERT (false);
314 
315  }
316  }
317 
318  return yRadius;
319 }
320 
321 void ExportFileFunctions::loadYRadiusValues (const DocumentModelExportFormat &modelExportOverride,
322  const Document &document,
323  const MainWindowModel &modelMainWindow,
324  const QStringList &curvesIncluded,
325  const Transformation &transformation,
326  bool isLogXTheta,
327  bool isLogYRadius,
328  const ExportValuesXOrY &xThetaValues,
329  const CurveLimits &curveLimitsMin,
330  const CurveLimits &curveLimitsMax,
331  QVector<QVector<QString*> > &yRadiusValues) const
332 {
333  LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::loadYRadiusValues";
334 
335  // Loop through curves
336  int curveCount = curvesIncluded.count();
337  for (int col = 0; col < curveCount; col++) {
338 
339  const QString curveName = curvesIncluded.at (col);
340 
341  const Curve *curve = document.curveForCurveName (curveName);
342  Points points = curve->points (); // These points will be linearized below if either coordinate is log
343 
344  if (modelExportOverride.pointsSelectionFunctions() == EXPORT_POINTS_SELECTION_FUNCTIONS_RAW) {
345 
346  // No interpolation. Raw points
347  loadYRadiusValuesForCurveRaw (document.modelCoords(),
348  document.modelGeneral(),
349  modelMainWindow,
350  points,
351  xThetaValues,
352  transformation,
353  curveName,
354  curveLimitsMin,
355  curveLimitsMax,
356  yRadiusValues [col]);
357  } else {
358 
359  // Interpolation
360  if (curve->curveStyle().lineStyle().curveConnectAs() == CONNECT_AS_FUNCTION_SMOOTH) {
361 
362  loadYRadiusValuesForCurveInterpolatedSmooth (document.modelCoords(),
363  document.modelGeneral(),
364  modelMainWindow,
365  points,
366  xThetaValues,
367  transformation,
368  isLogXTheta,
369  isLogYRadius,
370  curveName,
371  curveLimitsMin,
372  curveLimitsMax,
373  yRadiusValues [col]);
374 
375  } else {
376 
377  loadYRadiusValuesForCurveInterpolatedStraight (document.modelCoords(),
378  document.modelGeneral(),
379  modelMainWindow,
380  points,
381  xThetaValues,
382  transformation,
383  curveName,
384  curveLimitsMin,
385  curveLimitsMax,
386  yRadiusValues [col]);
387  }
388  }
389  }
390 }
391 
392 void ExportFileFunctions::loadYRadiusValuesForCurveInterpolatedSmooth (const DocumentModelCoords &modelCoords,
393  const DocumentModelGeneral &modelGeneral,
394  const MainWindowModel &modelMainWindow,
395  const Points &points,
396  const ExportValuesXOrY &xThetaValues,
397  const Transformation &transformation,
398  bool isLogXTheta,
399  bool isLogYRadius,
400  const QString &curveName,
401  const CurveLimits &curveLimitsMin,
402  const CurveLimits &curveLimitsMax,
403  QVector<QString*> &yRadiusValues) const
404 {
405  LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::loadYRadiusValuesForCurveInterpolatedSmooth";
406 
407  // Convert screen coordinates to graph coordinates, in vectors suitable for spline fitting
408  vector<double> t;
409  vector<SplinePair> xy;
410  ExportOrdinalsSmooth ordinalsSmooth;
411 
412  ordinalsSmooth.loadSplinePairsWithTransformation (points,
413  transformation,
414  isLogXTheta,
415  isLogYRadius,
416  t,
417  xy);
418 
419  // Formatting
420  FormatCoordsUnits format;
421  QString dummyXThetaOut;
422 
423  if (points.count() == 0) {
424 
425  // Since there are no values, leave the field empty
426  for (int row = 0; row < xThetaValues.count(); row++) {
427  *(yRadiusValues [row]) = "";
428  }
429 
430  } else if (points.count() == 1 ||
431  points.count() == 2) {
432 
433  // Apply the single value everywhere (N=1) or do linear interpolation (N=2)
434  for (int row = 0; row < xThetaValues.count(); row++) {
435 
436  double xTheta = xThetaValues.at (row);
437  double yRadius;
438  if (points.count() == 1) {
439  yRadius = xy.at (0).y ();
440  } else {
441  double x0 = xy.at (0).x ();
442  double x1 = xy.at (1).x ();
443  double y0 = xy.at (0).y ();
444  double y1 = xy.at (1).y ();
445  double numerator = (xTheta - x0);
446  double denominator = (x1 - x0);
447  if (qAbs (denominator) < qAbs (numerator) / 1.0e6) {
448  // Cannot do linear interpolation using two points at the same x value
449  yRadius = xy.at (0).y ();
450  } else {
451  double s = numerator / denominator;
452  yRadius = (1.0 - s) * y0 + s * y1;
453  }
454  }
455  if (xThetaIsNotOutOfBounds (xTheta,
456  curveName,
457  curveLimitsMin,
458  curveLimitsMax)) {
459  format.unformattedToFormatted (xTheta,
460  yRadius,
461  modelCoords,
462  modelGeneral,
463  modelMainWindow,
464  dummyXThetaOut,
465  *(yRadiusValues [row]),
466  transformation);
467  } else {
468  *(yRadiusValues [row]) = "";
469  }
470  }
471 
472  } else {
473 
474  // Iteration accuracy versus number of iterations 8->256, 10->1024, 12->4096. Single pixel accuracy out of
475  // typical image size of 1024x1024 means around 10 iterations gives decent accuracy for numbers much bigger
476  // than 1. A value of 12 gave some differences in the least significant figures of numbers like 10^-3 in
477  // the regression tests. Toggling between 30 and 32 made no difference in the regression tests.
478  const int MAX_ITERATIONS = 32;
479 
480  // Spline class requires at least one point
481  if (xy.size() > 0) {
482 
483  // Fit a spline
484  Spline spline (t,
485  xy);
486 
487  // Get value at desired points
488  for (int row = 0; row < xThetaValues.count(); row++) {
489 
490  double xTheta = xThetaValues.at (row);
491 
492  LinearToLog linearToLog;
493 
494  SplinePair splinePairFound = spline.findSplinePairForFunctionX (linearToLog.linearize (xTheta, isLogXTheta),
495  MAX_ITERATIONS);
496  double yRadius = linearToLog.delinearize (splinePairFound.y (),
497  isLogYRadius);
498 
499  // Save y/radius value for this row into yRadiusValues, after appropriate formatting
500  if (xThetaIsNotOutOfBounds (xTheta,
501  curveName,
502  curveLimitsMin,
503  curveLimitsMax)) {
504  format.unformattedToFormatted (xTheta,
505  yRadius,
506  modelCoords,
507  modelGeneral,
508  modelMainWindow,
509  dummyXThetaOut,
510  *(yRadiusValues [row]),
511  transformation);
512  } else {
513  *(yRadiusValues [row]) = "";
514  }
515  }
516  }
517  }
518 }
519 
520 void ExportFileFunctions::loadYRadiusValuesForCurveInterpolatedStraight (const DocumentModelCoords &modelCoords,
521  const DocumentModelGeneral &modelGeneral,
522  const MainWindowModel &modelMainWindow,
523  const Points &points,
524  const ExportValuesXOrY &xThetaValues,
525  const Transformation &transformation,
526  const QString &curveName,
527  const CurveLimits &curveLimitsMin,
528  const CurveLimits &curveLimitsMax,
529  QVector<QString*> &yRadiusValues) const
530 {
531  LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::loadYRadiusValuesForCurveInterpolatedStraight";
532 
533  FormatCoordsUnits format;
534 
535  // Get value at desired points
536  QString dummyXThetaOut;
537  for (int row = 0; row < xThetaValues.count(); row++) {
538 
539  double xTheta = xThetaValues.at (row);
540 
541  // Interpolation on curve with no points will trigger an assert so check the point count
542  *(yRadiusValues [row]) = "";
543  if (points.count () > 0) {
544 
545  double yRadius = linearlyInterpolate (points,
546  xTheta,
547  transformation);
548 
549  // Save y/radius value for this row into yRadiusValues, after appropriate formatting
550  if (xThetaIsNotOutOfBounds (xTheta,
551  curveName,
552  curveLimitsMin,
553  curveLimitsMax)) {
554  format.unformattedToFormatted (xTheta,
555  yRadius,
556  modelCoords,
557  modelGeneral,
558  modelMainWindow,
559  dummyXThetaOut,
560  *(yRadiusValues [row]),
561  transformation);
562  }
563  }
564  }
565 }
566 
567 void ExportFileFunctions::loadYRadiusValuesForCurveRaw (const DocumentModelCoords &modelCoords,
568  const DocumentModelGeneral &modelGeneral,
569  const MainWindowModel &modelMainWindow,
570  const Points &points,
571  const ExportValuesXOrY &xThetaValues,
572  const Transformation &transformation,
573  const QString &curveName,
574  const CurveLimits &curveLimitsMin,
575  const CurveLimits &curveLimitsMax,
576  QVector<QString*> &yRadiusValues) const
577 {
578  LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::loadYRadiusValuesForCurveRaw";
579 
580  FormatCoordsUnits format;
581 
582  // Since the curve points may be a subset of xThetaValues (in which case the non-applicable xThetaValues will have
583  // blanks for the yRadiusValues), we iterate over the smaller set
584  for (int pt = 0; pt < points.count(); pt++) {
585 
586  const Point &point = points.at (pt);
587 
588  QPointF posGraph;
589  transformation.transformScreenToRawGraph (point.posScreen(),
590  posGraph);
591 
592  // Find the closest point in xThetaValues. This is probably an N-squared algorithm, which is less than optimial,
593  // but the delay should be insignificant with normal-sized export files
594  double closestSeparation = 0.0;
595  int rowClosest = 0;
596  for (int row = 0; row < xThetaValues.count(); row++) {
597 
598  double xTheta = xThetaValues.at (row);
599 
600  double separation = qAbs (posGraph.x() - xTheta);
601 
602  if ((row == 0) ||
603  (separation < closestSeparation)) {
604 
605  closestSeparation = separation;
606  rowClosest = row;
607 
608  }
609  }
610 
611  // Save y/radius value for this row into yRadiusValues, after appropriate formatting
612  if (xThetaIsNotOutOfBounds (posGraph.x(),
613  curveName,
614  curveLimitsMin,
615  curveLimitsMax)) {
616  QString dummyXThetaOut;
617  format.unformattedToFormatted (posGraph.x(),
618  posGraph.y(),
619  modelCoords,
620  modelGeneral,
621  modelMainWindow,
622  dummyXThetaOut,
623  *(yRadiusValues [rowClosest]),
624  transformation);
625  } else {
626  *(yRadiusValues [rowClosest]) = "";
627  }
628  }
629 }
630 
631 void ExportFileFunctions::outputXThetaYRadiusValues (const DocumentModelExportFormat &modelExportOverride,
632  const DocumentModelCoords &modelCoords,
633  const DocumentModelGeneral &modelGeneral,
634  const MainWindowModel &modelMainWindow,
635  const QStringList &curvesIncluded,
636  const ExportValuesXOrY &xThetaValuesMerged,
637  const Transformation &transformation,
638  QVector<QVector<QString*> > &yRadiusValues,
639  const QString &delimiter,
640  QTextStream &str,
641  unsigned int &numWritesSoFar) const
642 {
643  LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::outputXThetaYRadiusValues";
644 
645  // Header
646  if (modelExportOverride.header() != EXPORT_HEADER_NONE) {
647  insertLineSeparator (numWritesSoFar == 0,
648  modelExportOverride.header (),
649  str);
650  if (modelExportOverride.header() == EXPORT_HEADER_GNUPLOT) {
651  str << gnuplotComment();
652  }
653  str << modelExportOverride.xLabel();
654  QStringList::const_iterator itrHeader;
655  for (itrHeader = curvesIncluded.begin(); itrHeader != curvesIncluded.end(); itrHeader++) {
656  QString curveName = *itrHeader;
657  str << delimiter << curveName;
658  }
659  str << "\n";
660  }
661 
662  // Table body
663  FormatCoordsUnits format;
664  const double DUMMY_Y_RADIUS = 1.0;
665 
666  for (int row = 0; row < xThetaValuesMerged.count(); row++) {
667 
668  if (rowHasAtLeastOneYRadiusEntry (yRadiusValues,
669  row)) {
670 
671  double xTheta = xThetaValuesMerged.at (row);
672 
673  // Output x/theta value for this row
674  QString xThetaString, yRadiusString;
675  format.unformattedToFormatted (xTheta,
676  DUMMY_Y_RADIUS,
677  modelCoords,
678  modelGeneral,
679  modelMainWindow,
680  xThetaString,
681  yRadiusString,
682  transformation);
683  str << wrapInDoubleQuotesIfNeeded (modelExportOverride,
684  xThetaString);
685 
686  for (int col = 0; col < yRadiusValues.count(); col++) {
687 
688  QString yRadiusString = *(yRadiusValues [col] [row]);
689  str << delimiter << wrapInDoubleQuotesIfNeeded (modelExportOverride,
690  yRadiusString);
691  }
692 
693  str << "\n";
694  }
695  }
696 
697  ++numWritesSoFar;
698 }
699 
700 bool ExportFileFunctions::rowHasAtLeastOneYRadiusEntry (const QVector<QVector<QString*> > &yRadiusValues,
701  int row) const
702 {
703  bool hasEntry = false;
704 
705  for (int col = 0; col < yRadiusValues.count(); col++) {
706 
707  QString entry = *(yRadiusValues [col] [row]);
708  if (!entry.isEmpty()) {
709 
710  hasEntry = true;
711  break;
712 
713  }
714  }
715 
716  return hasEntry;
717 }
718 
719 bool ExportFileFunctions::xThetaIsNotOutOfBounds (double xTheta,
720  const QString &curveName,
721  const CurveLimits &curveLimitsMin,
722  const CurveLimits &curveLimitsMax) const
723 {
724  bool ok = true;
725 
726  if (curveLimitsMin.contains (curveName)) {
727  ok = ok && (curveLimitsMin [curveName] <= xTheta);
728  }
729 
730  if (curveLimitsMax.contains (curveName)) {
731  ok = ok && (xTheta <= curveLimitsMax [curveName]);
732  }
733 
734  return ok;
735 }
Model for DlgSettingsGeneral and CmdSettingsGeneral.
CurveConnectAs curveConnectAs() const
Get method for connect type.
Definition: LineStyle.cpp:63
virtual CallbackSearchReturn callback(const QString &curveName, const Point &point)
Callback method.
Creates the set of merged x/theta values for exporting functions, using interpolation.
Cubic interpolation given independent and dependent value vectors.
Definition: Spline.h:29
ExportValuesXOrY xThetaValues() const
Resulting x/theta values for all included functions.
double y() const
Get method for y.
Definition: SplinePair.cpp:88
ValuesVectorXOrY xThetaValuesRaw() const
Resulting x/theta values for all included functions.
Callback for collecting X/Theta independent variables, for functions, in preparation for exporting,...
Model for DlgSettingsExportFormat and CmdSettingsExportFormat.
double linearize(double value, bool isLog) const
Convert log coordinates to linear. This is a noop if the input is already linear.
Definition: LinearToLog.cpp:19
double delinearize(double value, bool isLog) const
Convert linear coordinates to log. This is a noop if the output is supposed to be linear.
Definition: LinearToLog.cpp:8
const Curve * curveForCurveName(const QString &curveName) const
See CurvesGraphs::curveForCurveNames, although this also works for AXIS_CURVE_NAME.
Definition: Document.cpp:335
ExportFileFunctions()
Single constructor.
DocumentModelGeneral modelGeneral() const
Get method for DocumentModelGeneral.
Definition: Document.cpp:723
Class that represents one digitized point. The screen-to-graph coordinate transformation is always ex...
Definition: Point.h:25
QPointF posScreen() const
Accessor for screen position.
Definition: Point.cpp:404
DocumentModelCoords modelCoords() const
Get method for DocumentModelCoords.
const Points points() const
Return a shallow copy of the Points.
Definition: Curve.cpp:451
void unformattedToFormatted(double xThetaUnformatted, double yRadiusUnformatted, const DocumentModelCoords &modelCoords, const DocumentModelGeneral &modelGeneral, const MainWindowModel &mainWindowModel, QString &xThetaFormatted, QString &yRadiusFormatted, const Transformation &transformation) const
Convert unformatted numeric value to formatted string. Transformation is used to determine best resol...
Affine transformation between screen and graph coordinates, based on digitized axis points.
Model for DlgSettingsMainWindow.
LineStyle lineStyle() const
Get method for LineStyle.
Definition: CurveStyle.cpp:26
Utility class to interpolate points spaced evenly along a piecewise defined curve with fitted spline.
CoordScale coordScaleXTheta() const
Get method for linear/log scale on x/theta.
ExportDelimiter delimiter() const
Get method for delimiter.
Model for DlgSettingsCoords and CmdSettingsCoords.
CurveLimits curveLimitsMax() const
Endpoint maxima for each curve, if extrapolation has been disabled.
Storage of one imported image and the data attached to that image.
Definition: Document.h:41
Container for one set of digitized Points.
Definition: Curve.h:33
virtual CallbackSearchReturn callback(const QString &curveName, const Point &point)
Callback method.
Highest-level wrapper around other Formats classes.
void loadSplinePairsWithTransformation(const Points &points, const Transformation &transformation, bool isLogXTheta, bool isLogYRadius, std::vector< double > &t, std::vector< SplinePair > &xy) const
Load t (=ordinal) and xy (=screen position) spline pairs, converting screen coordinates to graph coor...
CoordScale coordScaleYRadius() const
Get method for linear/log scale on y/radius.
ExportHeader header() const
Get method for header.
CurveLimits curveLimitsMin() const
Endpoint minima for each curve, if extrapolation has been disabled.
ExportLayoutFunctions layoutFunctions() const
Get method for functions layout.
void transformScreenToRawGraph(const QPointF &coordScreen, QPointF &coordGraph) const
Transform from cartesian pixel screen coordinates to cartesian/polar graph coordinates.
QString xLabel() const
Get method for x label.
Callback for collecting X/Theta independent variables, for functions, in preparation for exporting,...
void iterateThroughCurvesPointsGraphs(const Functor2wRet< const QString &, const Point &, CallbackSearchReturn > &ftorWithCallback)
See Curve::iterateThroughCurvePoints, for all the graphs curves.
Definition: Document.cpp:472
CurveStyle curveStyle() const
Return the curve style.
Definition: Curve.cpp:149
Warps log coordinates to make them linear before passing them to code that accepts only linear coordi...
Definition: LinearToLog.h:7
ExportPointsSelectionFunctions pointsSelectionFunctions() const
Get method for point selection for functions.
DocumentModelCoords modelCoords() const
Get method for DocumentModelCoords.
Definition: Document.cpp:695
QStringList curvesGraphsNames() const
See CurvesGraphs::curvesGraphsNames.
Definition: Document.cpp:349
Single X/Y pair for cubic spline interpolation initialization and calculations.
Definition: SplinePair.h:13
void exportToFile(const DocumentModelExportFormat &modelExportOverride, const Document &document, const MainWindowModel &modelMainWindow, const Transformation &transformation, QTextStream &str, unsigned int &numWritesSoFar) const
Export Document points according to the settings.