Engauge Digitizer 2
Loading...
Searching...
No Matches
ExportFileFunctions.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
9#include "CurveConnectAs.h"
10#include "Document.h"
12#include "EngaugeAssert.h"
13#include "ExportFileFunctions.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
29using namespace std;
30
34
35void 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
83void 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,
144 bool &isOverrun) const
145{
146 LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::exportToFile";
147
148 isOverrun = false;
149
150 // Log coordinates must be temporarily transformed to linear coordinates
151 bool isLogXTheta = (document.modelCoords().coordScaleXTheta() == COORD_SCALE_LOG);
152 bool isLogYRadius = (document.modelCoords().coordScaleYRadius() == COORD_SCALE_LOG);
153
154 // Identify curves to be included
155 QStringList curvesIncluded = curvesToInclude (modelExportOverride,
156 document,
157 document.curvesGraphsNames(),
160
161 // Delimiter
162 const QString delimiter = exportDelimiterToText (modelExportOverride.delimiter(),
163 modelExportOverride.header() == EXPORT_HEADER_GNUPLOT);
164
165 // Get x/theta values to be used. Also get the endpoint limits, if any
166 CurveLimits curveLimitsMin, curveLimitsMax;
167 ValuesVectorXOrY valuesVector;
169 CallbackGatherXThetasInGridLines ftor (modelMainWindow,
170 modelExportOverride,
171 curvesIncluded,
172 transformation,
173 document);
174 Functor2wRet<const QString &, const Point &, CallbackSearchReturn> ftorWithCallback = functor_ret (ftor,
176 document.iterateThroughCurvesPointsGraphs(ftorWithCallback);
177 valuesVector = ftor.xThetaValuesRaw();
178 curveLimitsMin = ftor.curveLimitsMin();
179 curveLimitsMax = ftor.curveLimitsMax();
180 } else {
181 CallbackGatherXThetasInCurves ftor (modelExportOverride,
182 curvesIncluded,
183 transformation);
184 Functor2wRet<const QString &, const Point &, CallbackSearchReturn> ftorWithCallback = functor_ret (ftor,
186 document.iterateThroughCurvesPointsGraphs(ftorWithCallback);
187 valuesVector = ftor.xThetaValuesRaw();
188 curveLimitsMin = ftor.curveLimitsMin();
189 curveLimitsMax = ftor.curveLimitsMax();
190 }
191
192 ExportXThetaValuesMergedFunctions exportXTheta (modelExportOverride,
193 modelMainWindow,
194 valuesVector,
195 transformation);
196
197 ExportValuesXOrY xThetaValuesMerged = exportXTheta.xThetaValues (isOverrun);
198
199 if (isOverrun) {
200
201 // Put note in output to explain why there are no points
202 str << QObject::tr ("Too many points");
203
204 } else {
205
206 // Skip if every curve was a relation
207 if (xThetaValuesMerged.count() > 0) {
208
209 // Export in one of two layouts
210 if (modelExportOverride.layoutFunctions() == EXPORT_LAYOUT_ALL_PER_LINE) {
211 exportAllPerLineXThetaValuesMerged (modelExportOverride,
212 document,
213 modelMainWindow,
214 curvesIncluded,
215 xThetaValuesMerged,
216 delimiter,
217 transformation,
218 isLogXTheta,
219 isLogYRadius,
220 curveLimitsMin,
221 curveLimitsMax,
222 str,
223 numWritesSoFar);
224 } else {
225 exportOnePerLineXThetaValuesMerged (modelExportOverride,
226 document,
227 modelMainWindow,
228 curvesIncluded,
229 xThetaValuesMerged,
230 delimiter,
231 transformation,
232 isLogXTheta,
233 isLogYRadius,
234 curveLimitsMin,
235 curveLimitsMax,
236 str,
237 numWritesSoFar);
238 }
239 }
240 }
241}
242
243void ExportFileFunctions::initializeYRadiusValues (const QStringList &curvesIncluded,
244 const ExportValuesXOrY &xThetaValuesMerged,
245 QVector<QVector<QString*> > &yRadiusValues) const
246{
247 LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::initializeYRadiusValues";
248
249 // Initialize every entry with empty string
250 int curveCount = curvesIncluded.count();
251 int xThetaCount = xThetaValuesMerged.count();
252 for (int row = 0; row < xThetaCount; row++) {
253 for (int col = 0; col < curveCount; col++) {
254 yRadiusValues [col] [row] = new QString;
255 }
256 }
257}
258
259double ExportFileFunctions::linearlyInterpolate (const FittingPointsConvenient &positionsLinearized,
260 double xThetaLinearized) const
261{
262 // LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::linearlyInterpolate";
263
264 // If point is within the range of the function points then interpolation will be used, otherwise
265 // extrapolation will be used
266 double yRadiusLinearized = 0;
267 QPointF posGraphBefore; // Not set until ip=1
268 bool foundIt = false;
269 FittingPointsConvenient::const_iterator itr;
270 for (itr = positionsLinearized.begin(); itr != positionsLinearized.end(); itr++) {
271 const QPointF posGraph = *itr;
272
273 // Cases where we have found it at this point in the code
274 // (1) interpolation case where (xBefore < xThetaValue < xAfter)
275 // (2) extrapolation case where (xThetaValue < xBefore < xAfter and ip=0) for which we delay finding it until ip=1 so we have
276 // two points for extrapolating. This case is why we have the subtle test for ip>0 in the next line
277 if (xThetaLinearized <= posGraph.x() && (itr != positionsLinearized.begin())) {
278
279 foundIt = true;
280
281 // Case 1 comments: xThetaValue is between posGraphBefore and posGraph. Note that if posGraph.x()=posGraphBefore.x() then
282 // previous iteration of loop would have been used for interpolation, and then the loop was exited. Range of s is 0<s<1
283 // Case 2 comments: Range of s is s<0
284 yRadiusLinearized = linearlyInterpolateYRadiusFromTwoPoints (xThetaLinearized,
285 posGraphBefore,
286 posGraph);
287
288 break;
289 }
290
291 posGraphBefore = posGraph;
292 }
293
294 if (!foundIt) {
295
296 if (positionsLinearized.count() > 1) {
297
298 // Extrapolation will be used since point is out of the range of the function points. Specifically, it is greater than the
299 // last x value in the function. Range of s is 1<s
300 int N = positionsLinearized.count();
301 const QPointF &pointBefore = positionsLinearized.at (N - 2);
302 const QPointF &pointLast = positionsLinearized.at (N - 1);
303 yRadiusLinearized = linearlyInterpolateYRadiusFromTwoPoints (xThetaLinearized,
304 pointBefore,
305 pointLast);
306
307 } else if (positionsLinearized.count() == 1) {
308
309 // Just use the single point
310 yRadiusLinearized = posGraphBefore.y();
311
312 } else {
313
314 ENGAUGE_ASSERT (false);
315
316 }
317 }
318
319 return yRadiusLinearized;
320}
321
322void ExportFileFunctions::loadYRadiusValues (const DocumentModelExportFormat &modelExportOverride,
323 const Document &document,
324 const MainWindowModel &modelMainWindow,
325 const QStringList &curvesIncluded,
326 const Transformation &transformation,
327 bool isLogXTheta,
328 bool isLogYRadius,
329 const ExportValuesXOrY &xThetaValues,
330 const CurveLimits &curveLimitsMin,
331 const CurveLimits &curveLimitsMax,
332 QVector<QVector<QString*> > &yRadiusValues) const
333{
334 LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::loadYRadiusValues";
335
336 // Loop through curves
337 int curveCount = curvesIncluded.count();
338 for (int col = 0; col < curveCount; col++) {
339
340 const QString curveName = curvesIncluded.at (col);
341
342 const Curve *curve = document.curveForCurveName (curveName);
343 Points points = curve->points (); // These points will be linearized below if either coordinate is log
344
346
347 // No interpolation. Just raw points. No transformation to/from linear space is required for log coordinates
348 loadYRadiusValuesForCurveRaw (document.modelCoords(),
349 document.modelGeneral(),
350 modelMainWindow,
351 points,
352 xThetaValues,
353 transformation,
354 curveName,
355 curveLimitsMin,
356 curveLimitsMax,
357 yRadiusValues [col]);
358 } else {
359
360 // Interpolation
362
363 // Transformation to/from linear space is required for log coordinates
364 loadYRadiusValuesForCurveInterpolatedSmooth (document.modelCoords(),
365 document.modelGeneral(),
366 modelMainWindow,
367 points,
368 xThetaValues,
369 transformation,
370 isLogXTheta,
371 isLogYRadius,
372 curveName,
373 curveLimitsMin,
374 curveLimitsMax,
375 yRadiusValues [col]);
376
377 } else {
378
379 // Transformation to/from linear space is required for log coordinates
380 loadYRadiusValuesForCurveInterpolatedStraight (document.modelCoords(),
381 document.modelGeneral(),
382 modelMainWindow,
383 points,
384 xThetaValues,
385 transformation,
386 isLogXTheta,
387 isLogYRadius,
388 curveName,
389 curveLimitsMin,
390 curveLimitsMax,
391 yRadiusValues [col]);
392 }
393 }
394 }
395}
396
397void ExportFileFunctions::loadYRadiusValuesForCurveInterpolatedSmooth (const DocumentModelCoords &modelCoords,
398 const DocumentModelGeneral &modelGeneral,
399 const MainWindowModel &modelMainWindow,
400 const Points &points,
401 const ExportValuesXOrY &xThetaValues,
402 const Transformation &transformation,
403 bool isLogXTheta,
404 bool isLogYRadius,
405 const QString &curveName,
406 const CurveLimits &curveLimitsMin,
407 const CurveLimits &curveLimitsMax,
408 QVector<QString*> &yRadiusValues) const
409{
410 LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::loadYRadiusValuesForCurveInterpolatedSmooth";
411
412 // Convert screen coordinates to graph coordinates, in vectors suitable for spline fitting
413 vector<double> t;
414 vector<SplinePair> xyLinearized;
415 ExportOrdinalsSmooth ordinalsSmooth;
416
417 // Create spline pairs with x/y coordinates in linearized coordinates and t as the independent variable
418 ordinalsSmooth.loadSplinePairsWithTransformation (points,
419 transformation,
420 isLogXTheta,
421 isLogYRadius,
422 t,
423 xyLinearized);
424
425 // Linearize/delinearize utility
426 LinearToLog linearToLog;
427
428 // Formatting
429 FormatCoordsUnits format;
430 QString dummyXThetaOut;
431
432 if (points.count() == 0) {
433
434 // Since there are no values, leave the field empty
435 for (int row = 0; row < xThetaValues.count(); row++) {
436 *(yRadiusValues [row]) = "";
437 }
438
439 } else if (points.count() == 1 ||
440 points.count() == 2) {
441
442 // Apply the single value everywhere (N=1) or do linear interpolation (N=2)
443 for (int row = 0; row < xThetaValues.count(); row++) {
444
445 // Compute unlinearized and linearized x/theta
446 double xTheta = xThetaValues.at (row);
447 double xThetaLinearized = linearToLog.linearize (xTheta,
448 isLogXTheta);
449
450 double yRadiusLinearized;
451 if (points.count() == 1) {
452 yRadiusLinearized = xyLinearized.at (0).y ();
453 } else {
454 double x0 = xyLinearized.at (0).x ();
455 double x1 = xyLinearized.at (1).x ();
456 double y0 = xyLinearized.at (0).y ();
457 double y1 = xyLinearized.at (1).y ();
458 double numerator = (xThetaLinearized - x0);
459 double denominator = (x1 - x0);
460 if (qAbs (denominator) < qAbs (numerator) / 1.0e6) {
461 // Cannot do linear interpolation using two points at the same x value
462 yRadiusLinearized = xyLinearized.at (0).y ();
463 } else {
464 double s = numerator / denominator;
465 yRadiusLinearized = (1.0 - s) * y0 + s * y1;
466 }
467 }
468
469 // Delinearize, which is a noop for linear coordinates
470 double yRadius = linearToLog.delinearize (yRadiusLinearized,
471 isLogYRadius);
472
473 if (xThetaIsNotOutOfBounds (xTheta,
474 curveName,
475 curveLimitsMin,
476 curveLimitsMax)) {
477 format.unformattedToFormatted (xTheta,
478 yRadius,
479 modelCoords,
480 modelGeneral,
481 modelMainWindow,
482 dummyXThetaOut,
483 *(yRadiusValues [row]),
484 transformation);
485 } else {
486 *(yRadiusValues [row]) = "";
487 }
488 }
489
490 } else {
491
492 // Iteration accuracy versus number of iterations 8->256, 10->1024, 12->4096. Single pixel accuracy out of
493 // typical image size of 1024x1024 means around 10 iterations gives decent accuracy for numbers much bigger
494 // than 1. A value of 12 gave some differences in the least significant figures of numbers like 10^-3 in
495 // the regression tests. Toggling between 30 and 32 made no difference in the regression tests.
496 const int MAX_ITERATIONS = 32;
497
498 // Spline class requires at least one point
499 if (xyLinearized.size() > 0) {
500
501 // Fit a spline
502 Spline spline (t,
503 xyLinearized);
504
505 // Get value at desired points
506 for (int row = 0; row < xThetaValues.count(); row++) {
507
508 // Compute unlinearized and linearized x/theta
509 double xTheta = xThetaValues.at (row);
510 double xThetaLinearized = linearToLog.linearize (xTheta,
511 isLogXTheta);
512
513 SplinePair splinePairFound = spline.findSplinePairForFunctionX (xThetaLinearized,
514 MAX_ITERATIONS);
515 double yRadiusLinearized = splinePairFound.y ();
516
517 // Delinearize, which is a noop for linear coordinates
518 double yRadius = linearToLog.delinearize (yRadiusLinearized,
519 isLogYRadius);
520
521 // Save y/radius value for this row into yRadiusValues, after appropriate formatting
522 if (xThetaIsNotOutOfBounds (xTheta,
523 curveName,
524 curveLimitsMin,
525 curveLimitsMax)) {
526 format.unformattedToFormatted (xTheta,
527 yRadius,
528 modelCoords,
529 modelGeneral,
530 modelMainWindow,
531 dummyXThetaOut,
532 *(yRadiusValues [row]),
533 transformation);
534 } else {
535 *(yRadiusValues [row]) = "";
536 }
537 }
538 }
539 }
540}
541
542void ExportFileFunctions::loadYRadiusValuesForCurveInterpolatedStraight (const DocumentModelCoords &modelCoords,
543 const DocumentModelGeneral &modelGeneral,
544 const MainWindowModel &modelMainWindow,
545 const Points &points,
546 const ExportValuesXOrY &xThetaValues,
547 const Transformation &transformation,
548 bool isLogXTheta,
549 bool isLogYRadius,
550 const QString &curveName,
551 const CurveLimits &curveLimitsMin,
552 const CurveLimits &curveLimitsMax,
553 QVector<QString*> &yRadiusValues) const
554{
555 LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::loadYRadiusValuesForCurveInterpolatedStraight";
556
557 FormatCoordsUnits format;
558
559 // Linearize/delinearize utility
560 LinearToLog linearToLog;
561
562 FittingPointsConvenient positionsLinearized = populateLinearizedFittingPositions (points,
563 transformation,
564 isLogXTheta,
565 isLogYRadius);
566
567 // Get value at desired points
568 QString dummyXThetaOut;
569 for (int row = 0; row < xThetaValues.count(); row++) {
570
571 // Compute unlinearized and linearized x/theta
572 double xTheta = xThetaValues.at (row);
573 double xThetaLinearized = linearToLog.linearize (xTheta,
574 isLogXTheta);
575
576 // Interpolation on curve with no points will trigger an assert so check the point count
577 *(yRadiusValues [row]) = "";
578 if (positionsLinearized.count () > 0) {
579
580 double yRadiusLinearized = linearlyInterpolate (positionsLinearized,
581 xThetaLinearized);
582 double yRadius = linearToLog.delinearize (yRadiusLinearized,
583 isLogYRadius);
584
585 // Save y/radius value for this row into yRadiusValues, after appropriate formatting
586 if (xThetaIsNotOutOfBounds (xTheta,
587 curveName,
588 curveLimitsMin,
589 curveLimitsMax)) {
590 format.unformattedToFormatted (xTheta,
591 yRadius,
592 modelCoords,
593 modelGeneral,
594 modelMainWindow,
595 dummyXThetaOut,
596 *(yRadiusValues [row]),
597 transformation);
598 }
599 }
600 }
601}
602
603void ExportFileFunctions::loadYRadiusValuesForCurveRaw (const DocumentModelCoords &modelCoords,
604 const DocumentModelGeneral &modelGeneral,
605 const MainWindowModel &modelMainWindow,
606 const Points &points,
607 const ExportValuesXOrY &xThetaValues,
608 const Transformation &transformation,
609 const QString &curveName,
610 const CurveLimits &curveLimitsMin,
611 const CurveLimits &curveLimitsMax,
612 QVector<QString*> &yRadiusValues) const
613{
614 LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::loadYRadiusValuesForCurveRaw";
615
616 FormatCoordsUnits format;
617
618 // Since the curve points may be a subset of xThetaValues (in which case the non-applicable xThetaValues will have
619 // blanks for the yRadiusValues), we iterate over the smaller set
620 for (int pt = 0; pt < points.count(); pt++) {
621
622 const Point &point = points.at (pt);
623
624 QPointF posGraph;
625 transformation.transformScreenToRawGraph (point.posScreen(),
626 posGraph);
627
628 // Find the closest point in xThetaValues. This is probably an N-squared algorithm, which is less than optimial,
629 // but the delay should be insignificant with normal-sized export files
630 double closestSeparation = 0.0;
631 int rowClosest = 0;
632 for (int row = 0; row < xThetaValues.count(); row++) {
633
634 double xTheta = xThetaValues.at (row);
635
636 double separation = qAbs (posGraph.x() - xTheta);
637
638 if ((row == 0) ||
639 (separation < closestSeparation)) {
640
641 closestSeparation = separation;
642 rowClosest = row;
643
644 }
645 }
646
647 // Save y/radius value for this row into yRadiusValues, after appropriate formatting
648 if (xThetaIsNotOutOfBounds (posGraph.x(),
649 curveName,
650 curveLimitsMin,
651 curveLimitsMax)) {
652 QString dummyXThetaOut;
653 format.unformattedToFormatted (posGraph.x(),
654 posGraph.y(),
655 modelCoords,
656 modelGeneral,
657 modelMainWindow,
658 dummyXThetaOut,
659 *(yRadiusValues [rowClosest]),
660 transformation);
661 } else {
662 *(yRadiusValues [rowClosest]) = "";
663 }
664 }
665}
666
667void ExportFileFunctions::outputXThetaYRadiusValues (const DocumentModelExportFormat &modelExportOverride,
668 const DocumentModelCoords &modelCoords,
669 const DocumentModelGeneral &modelGeneral,
670 const MainWindowModel &modelMainWindow,
671 const QStringList &curvesIncluded,
672 const ExportValuesXOrY &xThetaValuesMerged,
673 const Transformation &transformation,
674 QVector<QVector<QString*> > &yRadiusValues,
675 const QString &delimiter,
676 QTextStream &str,
677 unsigned int &numWritesSoFar) const
678{
679 LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::outputXThetaYRadiusValues";
680
681 // Header
682 if (modelExportOverride.header() != EXPORT_HEADER_NONE) {
683 insertLineSeparator (numWritesSoFar == 0,
684 modelExportOverride.header (),
685 str);
686 if (modelExportOverride.header() == EXPORT_HEADER_GNUPLOT) {
687 str << gnuplotComment();
688 }
689 str << modelExportOverride.xLabel();
690 QStringList::const_iterator itrHeader;
691 for (itrHeader = curvesIncluded.begin(); itrHeader != curvesIncluded.end(); itrHeader++) {
692 QString curveName = *itrHeader;
693 str << delimiter << curveName;
694 }
695 str << "\n";
696 }
697
698 // Table body
699 FormatCoordsUnits format;
700 const double DUMMY_Y_RADIUS = 1.0;
701
702 for (int row = 0; row < xThetaValuesMerged.count(); row++) {
703
704 if (rowHasAtLeastOneYRadiusEntry (yRadiusValues,
705 row)) {
706
707 double xTheta = xThetaValuesMerged.at (row);
708
709 // Output x/theta value for this row
710 QString xThetaString, yRadiusString;
711 format.unformattedToFormatted (xTheta,
712 DUMMY_Y_RADIUS,
713 modelCoords,
714 modelGeneral,
715 modelMainWindow,
716 xThetaString,
717 yRadiusString,
718 transformation);
719 str << wrapInDoubleQuotesIfNeeded (modelExportOverride,
720 xThetaString);
721
722 for (int col = 0; col < yRadiusValues.count(); col++) {
723
724 QString yRadiusString = *(yRadiusValues [col] [row]);
725 str << delimiter << wrapInDoubleQuotesIfNeeded (modelExportOverride,
726 yRadiusString);
727 }
728
729 str << "\n";
730 }
731 }
732
733 ++numWritesSoFar;
734}
735
736FittingPointsConvenient ExportFileFunctions::populateLinearizedFittingPositions (const Points &points,
737 const Transformation &transformation,
738 bool isLogXTheta,
739 bool isLogYRadius) const
740{
741 LinearToLog linearToLog;
742
743 FittingPointsConvenient positionsLinearized;
744 Points::const_iterator itr;
745 for (itr = points.begin(); itr != points.end(); itr++) {
746 const Point &point = *itr;
747 QPointF posScreen = point.posScreen();
748 QPointF posGraph;
749 transformation.transformScreenToRawGraph (posScreen,
750 posGraph);
751
752 QPointF positionLinearized (linearToLog.linearize (posGraph.x(),
753 isLogXTheta),
754 linearToLog.linearize (posGraph.y(),
755 isLogYRadius));
756 positionsLinearized.push_back (positionLinearized);
757 }
758
759 return positionsLinearized;
760}
761
762bool ExportFileFunctions::rowHasAtLeastOneYRadiusEntry (const QVector<QVector<QString*> > &yRadiusValues,
763 int row) const
764{
765 bool hasEntry = false;
766
767 for (int col = 0; col < yRadiusValues.count(); col++) {
768
769 QString entry = *(yRadiusValues [col] [row]);
770 if (!entry.isEmpty()) {
771
772 hasEntry = true;
773 break;
774
775 }
776 }
777
778 return hasEntry;
779}
780
781bool ExportFileFunctions::xThetaIsNotOutOfBounds (double xTheta,
782 const QString &curveName,
783 const CurveLimits &curveLimitsMin,
784 const CurveLimits &curveLimitsMax) const
785{
786 bool ok = true;
787
788 if (curveLimitsMin.contains (curveName)) {
789 ok = ok && (curveLimitsMin [curveName] <= xTheta);
790 }
791
792 if (curveLimitsMax.contains (curveName)) {
793 ok = ok && (xTheta <= curveLimitsMax [curveName]);
794 }
795
796 return ok;
797}
@ COORD_SCALE_LOG
Definition CoordScale.h:14
@ CONNECT_AS_FUNCTION_STRAIGHT
@ CONNECT_AS_FUNCTION_SMOOTH
QHash< QString, double > CurveLimits
Definition CurveLimits.h:14
#define ENGAUGE_ASSERT(cond)
Drop in replacement for Q_ASSERT.
QString exportDelimiterToText(ExportDelimiter exportDelimiter, bool isGnuplotDelimiter)
@ EXPORT_HEADER_NONE
@ EXPORT_HEADER_GNUPLOT
@ EXPORT_LAYOUT_ALL_PER_LINE
@ EXPORT_POINTS_SELECTION_FUNCTIONS_RAW
@ EXPORT_POINTS_SELECTION_FUNCTIONS_INTERPOLATE_GRID_LINES
QList< double > ExportValuesXOrY
QList< QPointF > FittingPointsConvenient
Array of (x,y) points in graph coordinates.
log4cpp::Category * mainCat
Definition Logger.cpp:14
QList< Point > Points
Definition Points.h:13
QMap< double, bool > ValuesVectorXOrY
CurveLimits curveLimitsMax() const
Endpoint maxima for each curve, if extrapolation has been disabled.
ValuesVectorXOrY xThetaValuesRaw() const
Resulting x/theta values for all included functions.
CurveLimits curveLimitsMin() const
Endpoint minima for each curve, if extrapolation has been disabled.
Callback for collecting X/Theta independent variables, for functions, in preparation for exporting,...
virtual CallbackSearchReturn callback(const QString &curveName, const Point &point)
Callback method.
Callback for collecting X/Theta independent variables, for functions, in preparation for exporting,...
virtual CallbackSearchReturn callback(const QString &curveName, const Point &point)
Callback method.
LineStyle lineStyle() const
Get method for LineStyle.
CurveStyle curveStyle() const
Return the curve style.
Definition Curve.cpp:149
const Points points() const
Return a shallow copy of the Points.
Definition Curve.cpp:455
Model for DlgSettingsCoords and CmdSettingsCoords.
Model for DlgSettingsExportFormat and CmdSettingsExportFormat.
ExportHeader header() const
Get method for header.
QString xLabel() const
Get method for x label.
ExportPointsSelectionFunctions pointsSelectionFunctions() const
Get method for point selection for functions.
ExportDelimiter delimiter() const
Get method for delimiter.
ExportLayoutFunctions layoutFunctions() const
Get method for functions layout.
Model for DlgSettingsGeneral and CmdSettingsGeneral.
Storage of one imported image and the data attached to that image.
Definition Document.h:44
DocumentModelGeneral modelGeneral() const
Get method for DocumentModelGeneral.
Definition Document.cpp:735
void iterateThroughCurvesPointsGraphs(const Functor2wRet< const QString &, const Point &, CallbackSearchReturn > &ftorWithCallback)
See Curve::iterateThroughCurvePoints, for all the graphs curves.
Definition Document.cpp:478
QStringList curvesGraphsNames() const
See CurvesGraphs::curvesGraphsNames.
Definition Document.cpp:355
DocumentModelCoords modelCoords() const
Get method for DocumentModelCoords.
Definition Document.cpp:707
const Curve * curveForCurveName(const QString &curveName) const
See CurvesGraphs::curveForCurveNames, although this also works for AXIS_CURVE_NAME.
Definition Document.cpp:341
void destroy2DArray(QVector< QVector< QString * > > &array) const
Deallocate memory for array.
double linearlyInterpolateYRadiusFromTwoPoints(double xThetaLinearized, const QPointF &posGraphBefore, const QPointF &posGraph) const
Interpolate (if xThetaValue is between posGraphBefore.x() and posGraph.x()) or extrapolate (if xTheta...
QString wrapInDoubleQuotesIfNeeded(const DocumentModelExportFormat &modelExportOverride, const QString &valueString) const
RFC 4180 says if values are delimited by a comma AND a value has commas in it (for locale like Englis...
QString gnuplotComment() const
Gnuplot comment delimiter.
QStringList curvesToInclude(const DocumentModelExportFormat &modelExportOverride, const Document &document, const QStringList &curvesGraphsNames, CurveConnectAs curveConnectAs1, CurveConnectAs curveConnectAs2) const
Identify curves to include in export. The specified DocumentModelExportFormat overrides same data in ...
void insertLineSeparator(bool isFirst, ExportHeader exportHeader, QTextStream &str) const
Insert line(s) between successive sets of curves.
ExportFileFunctions()
Single constructor.
void exportToFile(const DocumentModelExportFormat &modelExportOverride, const Document &document, const MainWindowModel &modelMainWindow, const Transformation &transformation, QTextStream &str, unsigned int &numWritesSoFar, bool &isOverrun) const
Export Document points according to the settings.
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...
Creates the set of merged x/theta values for exporting functions, using interpolation.
ExportValuesXOrY xThetaValues(bool &isOverrun) const
Resulting x/theta values for all included functions.
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...
CurveConnectAs curveConnectAs() const
Get method for connect type.
Definition LineStyle.cpp:63
double delinearize(double value, bool isLog) const
Convert linear coordinates to log. This is a noop if the output is supposed to be linear.
double linearize(double value, bool isLog) const
Convert log coordinates to linear. This is a noop if the input is already linear.
Model for DlgSettingsMainWindow.
QPointF posScreen() const
Accessor for screen position.
Definition Point.cpp:404
double y() const
Get method for y.
Affine transformation between screen and graph coordinates, based on digitized axis points.
void transformScreenToRawGraph(const QPointF &coordScreen, QPointF &coordGraph) const
Transform from cartesian pixel screen coordinates to cartesian/polar graph coordinates.
#define LOG4CPP_INFO_S(logger)
Definition convenience.h:18