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