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