1 /******************************************************************************************************
2 * (C) 2016 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 "EngaugeAssert.h"
8 #include "FormatCoordsUnits.h"
9 #include "GeometryStrategyAbstractBase.h"
10 #include <qmath.h>
11 #include <QPointF>
12 #include "Spline.h"
13 #include "SplinePair.h"
14 #include "Transformation.h"
15 #include <vector>
16
17 using namespace std;
18
GeometryStrategyAbstractBase()19 GeometryStrategyAbstractBase::GeometryStrategyAbstractBase()
20 {
21 }
22
~GeometryStrategyAbstractBase()23 GeometryStrategyAbstractBase::~GeometryStrategyAbstractBase()
24 {
25 }
26
calculatePositionsGraph(const Points & points,const Transformation & transformation,QVector<QPointF> & positionsGraph) const27 void GeometryStrategyAbstractBase::calculatePositionsGraph (const Points &points,
28 const Transformation &transformation,
29 QVector<QPointF> &positionsGraph) const
30 {
31 positionsGraph.clear();
32
33 for (int i = 0; i < points.size(); i++) {
34 const Point &pointScreen = points [i];
35 QPointF posScreen = pointScreen.posScreen ();
36 QPointF posGraph;
37
38 transformation.transformScreenToRawGraph (posScreen,
39 posGraph);
40
41 positionsGraph.push_back (posGraph);
42 }
43 }
44
functionArea(const QVector<QPointF> & positionsGraph) const45 double GeometryStrategyAbstractBase::functionArea (const QVector<QPointF> &positionsGraph) const
46 {
47 // Integrate using trapezoidal approximation to get the area under the function
48 double sum = 0, xLast = 0, yLast = 0;
49 for (int i = 1; i < positionsGraph.size (); i++) {
50 double x = positionsGraph [i].x();
51 double y = positionsGraph [i].y();
52 double area = 0.5 * (y + yLast) * (x - xLast);
53 sum += area;
54 xLast = x;
55 yLast = y;
56 }
57
58 return sum;
59 }
60
insertSubintervalsAndLoadDistances(int subintervalsPerInterval,const QVector<QPointF> & positionsGraph,QVector<QPointF> & positionsGraphWithSubintervals,QVector<QString> & distanceGraphForward,QVector<QString> & distancePercentForward,QVector<QString> & distanceGraphBackward,QVector<QString> & distancePercentBackward) const61 void GeometryStrategyAbstractBase::insertSubintervalsAndLoadDistances (int subintervalsPerInterval,
62 const QVector<QPointF> &positionsGraph,
63 QVector<QPointF> &positionsGraphWithSubintervals,
64 QVector<QString> &distanceGraphForward,
65 QVector<QString> &distancePercentForward,
66 QVector<QString> &distanceGraphBackward,
67 QVector<QString> &distancePercentBackward) const
68 {
69 if (positionsGraph.size () > 0) {
70
71 int i;
72
73 // Fit splines to the points
74 vector<double> t;
75 vector<SplinePair> xy;
76 for (int i = 0; i < positionsGraph.size (); i++) {
77 t.push_back (double (i));
78 xy.push_back (SplinePair (positionsGraph [i].x(),
79 positionsGraph [i].y()));
80 }
81
82 Spline spline (t,
83 xy);
84
85 // Loop over the original points, with one original point per original interval
86 QVector<double> distanceGraphDouble;
87 double xLast = 0, yLast = 0, distance = 0;
88 for (i = 0; i < positionsGraph.size(); i++) {
89
90 // In the interval i-1 to i we insert points to create smaller subintervals
91 for (int subinterval = 0; subinterval < subintervalsPerInterval; subinterval++) {
92
93 // Go from i-1 (exclusive) to i (inclusive)
94 double t = double (i - 1.0) + double (subinterval + 1) / double (subintervalsPerInterval);
95
96 SplinePair splinePair = spline.interpolateCoeff (t);
97
98 double x = splinePair.x ();
99 double y = splinePair.y ();
100
101 // All points from intervals where i>0, and last point from interval i=0
102 if (i > 0 || subinterval == subintervalsPerInterval - 1) {
103
104 // Insert one of several new points for each original point
105 positionsGraphWithSubintervals.push_back (QPointF (x, y));
106
107 }
108
109 if (i > 0) {
110
111 // Add to cumulative distance
112 distance += qSqrt ((x - xLast) * (x - xLast) + (y - yLast) * (y - yLast));
113
114 }
115
116 xLast = x;
117 yLast = y;
118 }
119
120 // Insert one distance entry for each original point
121 distanceGraphDouble.push_back (distance);
122 }
123
124 // Compute distance columns
125 double dTotal = qMax (1.0, distanceGraphDouble [distanceGraphDouble.size() - 1]); // qMax prevents divide by zero
126 for (i = 0; i < distanceGraphDouble.size (); i++) {
127 double d = distanceGraphDouble [i];
128 distanceGraphForward.push_back (QString::number (d));
129 distancePercentForward.push_back (QString::number (100.0 * d / dTotal));
130 distanceGraphBackward.push_back (QString::number (dTotal - d));
131 distancePercentBackward.push_back (QString::number (100.0 * (dTotal - d) / dTotal));
132 }
133 }
134 }
135
loadPotentialExportVector(QVector<QString> & x,QVector<QString> &,const Transformation &,QVector<bool> & isPotentialExportAmbiguity) const136 void GeometryStrategyAbstractBase::loadPotentialExportVector (QVector<QString> &x,
137 QVector<QString> & /* y */,
138 const Transformation & /* transformation */,
139 QVector<bool> &isPotentialExportAmbiguity) const
140 {
141 for (int i = 0; i < x.size(); i++) {
142 isPotentialExportAmbiguity.append (false);
143 }
144 }
145
loadXY(const QVector<QPointF> & positionsGraph,const DocumentModelCoords & modelCoords,const DocumentModelGeneral & modelGeneral,const MainWindowModel & modelMainWindow,const Transformation & transformation,QVector<QString> & x,QVector<QString> & y) const146 void GeometryStrategyAbstractBase::loadXY (const QVector<QPointF> &positionsGraph,
147 const DocumentModelCoords &modelCoords,
148 const DocumentModelGeneral &modelGeneral,
149 const MainWindowModel &modelMainWindow,
150 const Transformation &transformation,
151 QVector<QString> &x,
152 QVector<QString> &y) const
153 {
154 FormatCoordsUnits formatCoordsUnits;
155
156 for (int i = 0; i < positionsGraph.size(); i++) {
157
158 double xI = positionsGraph [i].x();
159 double yI = positionsGraph [i].y();
160
161 QString xFormatted, yFormatted;
162 formatCoordsUnits.unformattedToFormatted (xI,
163 yI,
164 modelCoords,
165 modelGeneral,
166 modelMainWindow,
167 xFormatted,
168 yFormatted,
169 transformation);
170 x.push_back (xFormatted);
171 y.push_back (yFormatted);
172
173 }
174 }
175
polygonAreaForSimplyConnected(const QVector<QPointF> & points) const176 double GeometryStrategyAbstractBase::polygonAreaForSimplyConnected (const QVector<QPointF> &points) const
177 {
178 // Shoelace formula
179 int N = points.size ();
180
181 double sum = 0.0;
182 if (N > 0) {
183
184
185 for (int i = 0; i < N - 1; i++) {
186 sum += points [i].x() * points [i + 1].y() - points [i + 1].x() * points [i].y();
187 }
188
189 sum += points [N - 1].x() * points [0].y() - points [0].x() * points [N - 1].y ();
190 }
191
192 return qAbs (sum) / 2.0;
193 }
194