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 "Curve.h"
8 #include "CurvesGraphs.h"
9 #include "CurveStyles.h"
10 #include "DocumentSerialize.h"
11 #include "EngaugeAssert.h"
12 #include "Logger.h"
13 #include "Point.h"
14 #include <qdebug.h>
15 #include <QTextStream>
16 #include <QXmlStreamWriter>
17 #include "Transformation.h"
18 #include "Xml.h"
19 
CurvesGraphs()20 CurvesGraphs::CurvesGraphs()
21 {
22 }
23 
addGraphCurveAtEnd(const Curve & curve)24 void CurvesGraphs::addGraphCurveAtEnd (const Curve &curve)
25 {
26   m_curvesGraphs.push_back (curve);
27 }
28 
addPoint(const Point & point)29 void CurvesGraphs::addPoint (const Point &point)
30 {
31   QString curveName = Point::curveNameFromPointIdentifier (point.identifier());
32 
33   Curve *curve = curveForCurveName (curveName);
34   curve->addPoint (point);
35 }
36 
curveForCurveName(const QString & curveName)37 Curve *CurvesGraphs::curveForCurveName (const QString &curveName)
38 {
39   // Search for curve with matching name
40   CurveList::iterator itr;
41   for (itr = m_curvesGraphs.begin (); itr != m_curvesGraphs.end (); itr++) {
42 
43     Curve &curve = *itr;
44     if (curveName == curve.curveName ()) {
45       return &curve;
46     }
47   }
48 
49   return nullptr;
50 }
51 
curveForCurveName(const QString & curveName) const52 const Curve *CurvesGraphs::curveForCurveName (const QString &curveName) const
53 {
54   // Search for curve with matching name
55   CurveList::const_iterator itr;
56   for (itr = m_curvesGraphs.begin (); itr != m_curvesGraphs.end (); itr++) {
57 
58     const Curve &curve = *itr;
59     if (curveName == curve.curveName ()) {
60       return &curve;
61     }
62   }
63 
64   return nullptr;
65 }
66 
curvesGraphsNames() const67 QStringList CurvesGraphs::curvesGraphsNames () const
68 {
69   QStringList names;
70 
71   CurveList::const_iterator itr;
72   for (itr = m_curvesGraphs.begin (); itr != m_curvesGraphs.end (); itr++) {
73 
74     const Curve &curve = *itr;
75     names << curve.curveName ();
76   }
77 
78   return names;
79 }
80 
curvesGraphsNumPoints(const QString & curveName) const81 int CurvesGraphs::curvesGraphsNumPoints (const QString &curveName) const
82 {
83   // Search for curve with matching name
84   CurveList::const_iterator itr;
85   for (itr = m_curvesGraphs.begin (); itr != m_curvesGraphs.end (); itr++) {
86 
87     const Curve &curve = *itr;
88     if (curve.curveName () == curveName) {
89       return curve.numPoints ();
90     }
91   }
92 
93   return 0;
94 }
95 
editPointGraph(bool isX,bool isY,double x,double y,const QStringList & identifiers,const Transformation & transformation)96 void CurvesGraphs::editPointGraph (bool isX,
97                                    bool isY,
98                                    double x,
99                                    double y,
100                                    const QStringList &identifiers,
101                                    const Transformation &transformation)
102 {
103   CurveList::iterator itr;
104   for (itr = m_curvesGraphs.begin (); itr != m_curvesGraphs.end (); itr++) {
105 
106     Curve &curve = *itr;
107     curve.editPointGraph (isX,
108                           isY,
109                           x,
110                           y,
111                           identifiers,
112                           transformation);
113   }
114 }
115 
iterateThroughCurvePoints(const QString & curveNameWanted,const Functor2wRet<const QString &,const Point &,CallbackSearchReturn> & ftorWithCallback)116 void CurvesGraphs::iterateThroughCurvePoints (const QString &curveNameWanted,
117                                               const Functor2wRet<const QString &, const Point &, CallbackSearchReturn> &ftorWithCallback)
118 {
119   // Search for curve with matching name
120   CurveList::const_iterator itr;
121   for (itr = m_curvesGraphs.begin (); itr != m_curvesGraphs.end (); itr++) {
122 
123     const Curve &curve = *itr;
124     if (curve.curveName () == curveNameWanted) {
125 
126       curve.iterateThroughCurvePoints (ftorWithCallback);
127       return;
128     }
129   }
130 
131   LOG4CPP_ERROR_S ((*mainCat)) << "CurvesGraphs::iterateThroughCurvePoints encountered unexpected curve "
132                                << curveNameWanted.toLatin1().data();
133   ENGAUGE_ASSERT (false);
134 }
135 
iterateThroughCurveSegments(const QString & curveNameWanted,const Functor2wRet<const Point &,const Point &,CallbackSearchReturn> & ftorWithCallback) const136 void CurvesGraphs::iterateThroughCurveSegments (const QString &curveNameWanted,
137                                                 const Functor2wRet<const Point &, const Point &, CallbackSearchReturn> &ftorWithCallback) const
138 {
139   // Search for curve with matching name
140   CurveList::const_iterator itr;
141   for (itr = m_curvesGraphs.begin (); itr != m_curvesGraphs.end (); itr++) {
142 
143     const Curve &curve = *itr;
144     if (curve.curveName () == curveNameWanted) {
145 
146       curve.iterateThroughCurveSegments (ftorWithCallback);
147       return;
148     }
149   }
150 
151   LOG4CPP_ERROR_S ((*mainCat)) << "CurvesGraphs::iterateThroughCurveSegments encountered unexpected curve "
152                                << curveNameWanted.toLatin1().data();
153   ENGAUGE_ASSERT (false);
154 }
155 
iterateThroughCurvesPoints(const Functor2wRet<const QString &,const Point &,CallbackSearchReturn> & ftorWithCallback)156 void CurvesGraphs::iterateThroughCurvesPoints (const Functor2wRet<const QString &, const Point &, CallbackSearchReturn> &ftorWithCallback)
157 {
158   CurveList::const_iterator itr;
159   for (itr = m_curvesGraphs.begin (); itr != m_curvesGraphs.end (); itr++) {
160 
161     const Curve &curve = *itr;
162     curve.iterateThroughCurvePoints (ftorWithCallback);
163   }
164 }
165 
iterateThroughCurvesPoints(const Functor2wRet<const QString &,const Point &,CallbackSearchReturn> & ftorWithCallback) const166 void CurvesGraphs::iterateThroughCurvesPoints (const Functor2wRet<const QString &, const Point &, CallbackSearchReturn> &ftorWithCallback)  const
167 {
168   CurveList::const_iterator itr;
169   for (itr = m_curvesGraphs.begin (); itr != m_curvesGraphs.end (); itr++) {
170 
171     const Curve &curve = *itr;
172     curve.iterateThroughCurvePoints (ftorWithCallback);
173   }
174 }
175 
loadPreVersion6(QDataStream & str)176 void CurvesGraphs::loadPreVersion6(QDataStream &str)
177 {
178   LOG4CPP_INFO_S ((*mainCat)) << "CurvesGraphs::loadPreVersion6";
179 
180   int i;
181 
182   // Remove previous Curves. There is a DEFAULT_GRAPH_CURVE_NAME by default
183   m_curvesGraphs.clear();
184 
185   qint32 numberCurvesGraphs;
186   str >> numberCurvesGraphs;
187   for (i = 0; i < numberCurvesGraphs; i++) {
188     Curve curve (str);
189     m_curvesGraphs.append (curve);
190   }
191 
192   qint32 numberCurvesMeasures;
193   str >> numberCurvesMeasures;
194   for (i = 0; i < numberCurvesMeasures; i++) {
195     Curve curve (str);
196 
197     // Measures get dropped on the floor
198   }
199 }
200 
loadXml(QXmlStreamReader & reader)201 void CurvesGraphs::loadXml(QXmlStreamReader &reader)
202 {
203   LOG4CPP_INFO_S ((*mainCat)) << "CurvesGraphs::loadXml";
204 
205   bool success = true;
206 
207   // Remove previous Curves. There is a DEFAULT_GRAPH_CURVE_NAME by default
208   m_curvesGraphs.clear();
209 
210   // Read until end of this subtree
211   while ((reader.tokenType() != QXmlStreamReader::EndElement) ||
212   (reader.name() != DOCUMENT_SERIALIZE_CURVES_GRAPHS)){
213 
214     loadNextFromReader(reader);
215     if (reader.atEnd()) {
216       success = false;
217       break;
218     }
219 
220     if ((reader.tokenType() == QXmlStreamReader::StartElement) &&
221         (reader.name () == DOCUMENT_SERIALIZE_CURVE)) {
222 
223       Curve curve (reader);
224 
225       // Version 6 of Engauge let users create multiple curves with the same name. Reading a file with duplicate
226       // curve names can result in crashes and/or corruption, so we deconflict duplicate curve names here
227       QString DUPLICATE = QString ("-%1").arg (QObject::tr ("DUPLICATE"));
228       QString curveName = curve.curveName();
229       while (curvesGraphsNames().contains (curveName)) {
230         curveName += DUPLICATE;
231       }
232       curve.setCurveName (curveName); // No effect if curve name was not a duplicate
233 
234       // Add the curve
235       m_curvesGraphs.push_back (curve);
236 
237     }
238   }
239 
240   if (!success) {
241     reader.raiseError (QObject::tr ("Cannot read graph curves data"));
242   }
243 }
244 
numCurves() const245 int CurvesGraphs::numCurves () const
246 {
247   return m_curvesGraphs.count ();
248 }
249 
printStream(QString indentation,QTextStream & str) const250 void CurvesGraphs::printStream (QString indentation,
251                                 QTextStream &str) const
252 {
253   str << indentation << "CurvesGraphs\n";
254 
255   indentation += INDENTATION_DELTA;
256 
257   CurveList::const_iterator itr;
258   for (itr = m_curvesGraphs.begin (); itr != m_curvesGraphs.end (); itr++) {
259 
260     const Curve &curve = *itr;
261     curve.printStream (indentation,
262                        str);
263   }
264 }
265 
removePoint(const QString & pointIdentifier)266 void CurvesGraphs::removePoint (const QString &pointIdentifier)
267 {
268   QString curveName = Point::curveNameFromPointIdentifier(pointIdentifier);
269 
270   Curve *curve = curveForCurveName (curveName);
271   curve->removePoint (pointIdentifier);
272 }
273 
saveXml(QXmlStreamWriter & writer) const274 void CurvesGraphs::saveXml(QXmlStreamWriter &writer) const
275 {
276   LOG4CPP_INFO_S ((*mainCat)) << "CurvesGraphs::saveXml";
277 
278   writer.writeStartElement(DOCUMENT_SERIALIZE_CURVES_GRAPHS);
279 
280   CurveList::const_iterator itr;
281   for (itr = m_curvesGraphs.begin (); itr != m_curvesGraphs.end (); itr++) {
282 
283     const Curve &curve = *itr;
284     curve.saveXml (writer);
285   }
286 
287   writer.writeEndElement();
288 }
289 
updatePointOrdinals(const Transformation & transformation)290 void CurvesGraphs::updatePointOrdinals (const Transformation &transformation)
291 {
292   LOG4CPP_INFO_S ((*mainCat)) << "CurvesGraphs::updatePointOrdinals";
293 
294   CurveList::iterator itr;
295   for (itr = m_curvesGraphs.begin (); itr != m_curvesGraphs.end (); itr++) {
296 
297     Curve &curve = *itr;
298     curve.updatePointOrdinals (transformation);
299   }
300 }
301