Engauge Digitizer  2
Curve.cpp
1 #include "Curve.h"
2 #include "CurvesGraphs.h"
3 #include "DocumentSerialize.h"
4 #include "EngaugeAssert.h"
5 #include "Logger.h"
6 #include "Point.h"
7 #include "PointComparator.h"
8 #include <QDebug>
9 #include <QMap>
10 #include <QTextStream>
11 #include <QXmlStreamReader>
12 #include <QXmlStreamWriter>
13 #include "Transformation.h"
14 #include "Xml.h"
15 
16 const QString AXIS_CURVE_NAME ("Axes");
17 const int AXIS_CURVE_ORDINAL = 0;
18 const QString DEFAULT_GRAPH_CURVE_NAME ("Curve1");
19 const QString TAB_DELIMITER ("\t");
20 
21 typedef QMap<double, QString> XOrThetaToPointIdentifier;
22 
23 Curve::Curve(const QString &curveName,
24  const ColorFilterSettings &colorFilterSettings,
25  const CurveStyle &curveStyle) :
26  m_curveName (curveName),
27  m_colorFilterSettings (colorFilterSettings),
28  m_curveStyle (curveStyle)
29 {
30 }
31 
32 Curve::Curve (const Curve &curve) :
33  m_curveName (curve.curveName ()),
34  m_points (curve.points ()),
35  m_colorFilterSettings (curve.colorFilterSettings ()),
36  m_curveStyle (curve.curveStyle ())
37 {
38 }
39 
40 Curve::Curve (QXmlStreamReader &reader)
41 {
42  loadXml(reader);
43 }
44 
46 {
47  m_curveName = curve.curveName ();
48  m_points = curve.points ();
49  m_colorFilterSettings = curve.colorFilterSettings ();
50  m_curveStyle = curve.curveStyle ();
51 
52  return *this;
53 }
54 
55 void Curve::addPoint (Point point)
56 {
57  m_points.push_back (point);
58 }
59 
61 {
62  return m_colorFilterSettings;
63 }
64 
65 QString Curve::curveName () const
66 {
67  return m_curveName;
68 }
69 
71 {
72  return m_curveStyle;
73 }
74 
75 void Curve::editPoint (const QPointF &posGraph,
76  const QString &identifier)
77 {
78  // Search for the point with matching identifier
79  QList<Point>::iterator itr;
80  for (itr = m_points.begin (); itr != m_points.end (); itr++) {
81 
82  Point &point = *itr;
83  if (point.identifier () == identifier) {
84 
85  point.setPosGraph (posGraph);
86  break;
87 
88  }
89  }
90 }
91 
92 void Curve::exportToClipboard (const QHash<QString, bool> &selectedHash,
93  const Transformation &transformation,
94  QTextStream &strCsv,
95  QTextStream &strHtml,
96  CurvesGraphs &curvesGraphs) const
97 {
98  LOG4CPP_INFO_S ((*mainCat)) << "Curve::exportToClipboard"
99  << " hashCount=" << selectedHash.count();
100 
101  // This method assumes Copy is only allowed when Transformation is valid
102 
103  bool isFirst = true;
104  QList<Point>::const_iterator itr;
105  for (itr = m_points.begin (); itr != m_points.end (); itr++) {
106 
107  const Point &point = *itr;
108  if (selectedHash.contains (point.identifier ())) {
109 
110  if (isFirst) {
111 
112  // Insert headers to identify the points that follow
113  isFirst = false;
114  strCsv << "X" << TAB_DELIMITER << m_curveName << "\n";
115  strHtml << "<table>\n"
116  << "<tr><th>X</th><th>" << m_curveName << "</th></tr>\n";
117  }
118 
119  // Default curve style
120  CurveStyle curveStyleDefault;
121  curveStyleDefault.setLineStyle(LineStyle::defaultAxesCurve());
122  curveStyleDefault.setPointStyle(PointStyle::defaultGraphCurve (curvesGraphs.numCurves ()));
123 
124  // Check if this curve already exists from a previously exported point
125  if (curvesGraphs.curveForCurveName (m_curveName) == 0) {
126  Curve curve(m_curveName,
128  curveStyleDefault);
129  curvesGraphs.addGraphCurveAtEnd(curve);
130  }
131 
132  // Start with screen coordinates
133  QPointF pos = point.posScreen();
134  if (transformation.transformIsDefined()) {
135 
136  // Replace with graph coordinates which are almost always more useful
137  QPointF posGraph;
138  transformation.transformScreenToRawGraph(pos,
139  posGraph);
140  pos = posGraph;
141  }
142 
143  // Add point to text going to clipboard
144  strCsv << pos.x() << TAB_DELIMITER << pos.y() << "\n";
145  strHtml << "<tr><td>" << pos.x() << "</td><td>" << pos.y() << "</td></tr>\n";
146 
147  // Add point to list for undo/redo
148  curvesGraphs.curveForCurveName (m_curveName)->addPoint (point);
149  }
150  }
151 
152  if (!isFirst) {
153  strHtml << "</table>\n";
154  }
155 }
156 
157 void Curve::iterateThroughCurvePoints (const Functor2wRet<const QString &, const Point&, CallbackSearchReturn> &ftorWithCallback) const
158 {
159  QList<Point>::const_iterator itr;
160  for (itr = m_points.begin (); itr != m_points.end (); itr++) {
161 
162  const Point &point = *itr;
163 
164  CallbackSearchReturn rtn = ftorWithCallback (m_curveName, point);
165 
167  break;
168  }
169  }
170 }
171 
172 void Curve::iterateThroughCurveSegments (const Functor2wRet<const Point&, const Point&, CallbackSearchReturn> &ftorWithCallback) const
173 {
174  // Loop through Points. They are assumed to be already sorted by their ordinals, but we do NOT
175  // check the ordinal ordering since this could be called before, or while, the ordinal sorting is done
176  QList<Point>::const_iterator itr;
177  const Point *pointBefore = 0;
178  for (itr = m_points.begin(); itr != m_points.end(); itr++) {
179 
180  const Point &point = *itr;
181 
182  if (pointBefore != 0) {
183 
184  CallbackSearchReturn rtn = ftorWithCallback (*pointBefore,
185  point);
186 
188  break;
189  }
190 
191  }
192  pointBefore = &point;
193  }
194 }
195 
196 void Curve::loadCurvePoints(QXmlStreamReader &reader)
197 {
198  LOG4CPP_INFO_S ((*mainCat)) << "Curve::loadCurvePoints";
199 
200  bool success = true;
201 
202  while ((reader.tokenType() != QXmlStreamReader::EndElement) ||
203  (reader.name() != DOCUMENT_SERIALIZE_CURVE_POINTS)) {
204 
205  QXmlStreamReader::TokenType tokenType = loadNextFromReader(reader);
206 
207  if (reader.atEnd()) {
208  success = false;
209  break;
210  }
211 
212  if (tokenType == QXmlStreamReader::StartElement) {
213 
214  if (reader.name () == DOCUMENT_SERIALIZE_POINT) {
215 
216  Point point (reader);
217  m_points.push_back (point);
218  }
219  }
220  }
221 
222  if (!success) {
223  reader.raiseError("Cannot read curve data");
224  }
225 }
226 
227 void Curve::loadXml(QXmlStreamReader &reader)
228 {
229  LOG4CPP_INFO_S ((*mainCat)) << "Curve::loadXml";
230 
231  bool success = true;
232 
233  QXmlStreamAttributes attributes = reader.attributes();
234 
235  if (attributes.hasAttribute (DOCUMENT_SERIALIZE_CURVE_NAME)) {
236 
237  setCurveName (attributes.value (DOCUMENT_SERIALIZE_CURVE_NAME).toString());
238 
239  // Read until end of this subtree
240  while ((reader.tokenType() != QXmlStreamReader::EndElement) ||
241  (reader.name() != DOCUMENT_SERIALIZE_CURVE)){
242 
243  QXmlStreamReader::TokenType tokenType = loadNextFromReader(reader);
244 
245  if (reader.atEnd()) {
246  success = false;
247  break;
248  }
249 
250  if (tokenType == QXmlStreamReader::StartElement) {
251 
252  if (reader.name() == DOCUMENT_SERIALIZE_COLOR_FILTER) {
253  m_colorFilterSettings.loadXml(reader);
254  } else if (reader.name() == DOCUMENT_SERIALIZE_CURVE_POINTS) {
255  loadCurvePoints(reader);
256  } else if (reader.name() == DOCUMENT_SERIALIZE_CURVE_STYLE) {
257  m_curveStyle.loadXml(reader);
258  } else {
259  success = false;
260  break;
261  }
262  }
263 
264  if (reader.hasError()) {
265  // No need to set success flag to indicate failure, which raises the error, since the error was already raised. Just
266  // need to exit the loop immediately
267  break;
268  }
269  }
270  } else {
271  success = false;
272  }
273 
274  if (!success) {
275  reader.raiseError ("Cannot read curve data");
276  }
277 }
278 
279 void Curve::movePoint (const QString &pointIdentifier,
280  const QPointF &deltaScreen)
281 {
282  Point *point = pointForPointIdentifier (pointIdentifier);
283 
284  QPointF posScreen = deltaScreen + point->posScreen ();
285  point->setPosScreen (posScreen);
286 }
287 
288 int Curve::numPoints () const
289 {
290  return m_points.count ();
291 }
292 
293 Point *Curve::pointForPointIdentifier (const QString pointIdentifier)
294 {
295  Points::iterator itr;
296  for (itr = m_points.begin (); itr != m_points.end (); itr++) {
297  Point &point = *itr;
298  if (pointIdentifier == point.identifier ()) {
299  return &point;
300  }
301  }
302 
303  ENGAUGE_ASSERT (false);
304  return 0;
305 }
306 
307 const Points Curve::points () const
308 {
309  return m_points;
310 }
311 
312 QPointF Curve::positionGraph (const QString &pointIdentifier) const
313 {
314  QPointF posGraph;
315 
316  // Search for point with matching identifier
317  Points::const_iterator itr;
318  for (itr = m_points.begin (); itr != m_points.end (); itr++) {
319  const Point &point = *itr;
320  if (pointIdentifier == point.identifier ()) {
321  posGraph = point.posGraph ();
322  break;
323  }
324  }
325 
326  return posGraph;
327 }
328 
329 QPointF Curve::positionScreen (const QString &pointIdentifier) const
330 {
331  QPointF posScreen;
332 
333  // Search for point with matching identifier
334  Points::const_iterator itr;
335  for (itr = m_points.begin (); itr != m_points.end (); itr++) {
336  const Point &point = *itr;
337  if (pointIdentifier == point.identifier ()) {
338  posScreen = point.posScreen ();
339  break;
340  }
341  }
342 
343  return posScreen;
344 }
345 
346 void Curve::printStream (QString indentation,
347  QTextStream &str) const
348 {
349  str << indentation << "Curve=" << m_curveName << "\n";
350 
351  indentation += INDENTATION_DELTA;
352 
353  Points::const_iterator itr;
354  for (itr = m_points.begin (); itr != m_points.end (); itr++) {
355  const Point &point = *itr;
356  point.printStream (indentation,
357  str);
358  }
359 
360  m_colorFilterSettings.printStream (indentation,
361  str);
362  m_curveStyle.printStream (indentation,
363  str);
364 }
365 
366 void Curve::removePoint (const QString &identifier)
367 {
368  // Search for point with matching identifier
369  Points::iterator itr;
370  for (itr = m_points.begin (); itr != m_points.end (); itr++) {
371  Point point = *itr;
372  if (point.identifier () == identifier) {
373  m_points.erase (itr);
374  break;
375  }
376  }
377 }
378 
379 void Curve::saveXml(QXmlStreamWriter &writer) const
380 {
381  LOG4CPP_INFO_S ((*mainCat)) << "Curve::saveXml";
382 
383  writer.writeStartElement(DOCUMENT_SERIALIZE_CURVE);
384  writer.writeAttribute(DOCUMENT_SERIALIZE_CURVE_NAME, m_curveName);
385  m_colorFilterSettings.saveXml (writer);
386  m_curveStyle.saveXml (writer,
387  m_curveName);
388 
389  // Loop through points
390  writer.writeStartElement(DOCUMENT_SERIALIZE_CURVE_POINTS);
391  Points::const_iterator itr;
392  for (itr = m_points.begin (); itr != m_points.end (); itr++) {
393  const Point &point = *itr;
394  point.saveXml (writer);
395  }
396  writer.writeEndElement();
397 
398  writer.writeEndElement();
399 }
400 
401 void Curve::setColorFilterSettings (const ColorFilterSettings &colorFilterSettings)
402 {
403  m_colorFilterSettings = colorFilterSettings;
404 }
405 
406 void Curve::setCurveName (const QString &curveName)
407 {
408  m_curveName = curveName;
409 }
410 
411 void Curve::setCurveStyle (const CurveStyle &curveStyle)
412 {
413  m_curveStyle = curveStyle;
414 }
415 
416 void Curve::updatePointOrdinals (const Transformation &transformation)
417 {
418  CurveConnectAs curveConnectAs = m_curveStyle.lineStyle().curveConnectAs();
419 
420  LOG4CPP_INFO_S ((*mainCat)) << "Curve::updatePointOrdinals"
421  << " curve=" << m_curveName.toLatin1().data()
422  << " connectAs=" << curveConnectAsToString(curveConnectAs).toLatin1().data();
423 
424  // Make sure ordinals are properly ordered. Sorting is done afterward
425 
426  if (curveConnectAs == CONNECT_AS_FUNCTION_SMOOTH ||
427  curveConnectAs == CONNECT_AS_FUNCTION_STRAIGHT) {
428 
429  updatePointOrdinalsFunctions (transformation);
430 
431  } else if (curveConnectAs == CONNECT_AS_RELATION_SMOOTH ||
432  curveConnectAs == CONNECT_AS_RELATION_STRAIGHT) {
433 
434  updatePointOrdinalsRelations ();
435 
436  } else {
437 
438  LOG4CPP_ERROR_S ((*mainCat)) << "Curve::updatePointOrdinals";
439  ENGAUGE_ASSERT (false);
440 
441  }
442 
443  qSort (m_points.begin(),
444  m_points.end(),
445  PointComparator());
446 }
447 
448 void Curve::updatePointOrdinalsFunctions (const Transformation &transformation)
449 {
450  CurveConnectAs curveConnectAs = m_curveStyle.lineStyle().curveConnectAs();
451 
452  LOG4CPP_INFO_S ((*mainCat)) << "Curve::updatePointOrdinalsFunctions"
453  << " curve=" << m_curveName.toLatin1().data()
454  << " connectAs=" << curveConnectAsToString(curveConnectAs).toLatin1().data();
455 
456  // Get a map of x/theta values as keys with point identifiers as the values
457  XOrThetaToPointIdentifier xOrThetaToPointIdentifier;
458  Points::iterator itr;
459  for (itr = m_points.begin (); itr != m_points.end (); itr++) {
460  Point &point = *itr;
461 
462  QPointF posGraph;
463  if (transformation.transformIsDefined()) {
464 
465  // Transformation is available so use it
466  transformation.transformScreenToRawGraph (point.posScreen (),
467  posGraph);
468  } else {
469 
470  // Transformation is not available so we just use the screen coordinates. Effectively, the
471  // transformation is the identity matrix
472  posGraph= point.posScreen();
473  }
474 
475  xOrThetaToPointIdentifier [posGraph.x()] = point.identifier();
476  }
477 
478  // Since m_points is a list (and therefore does not provide direct access to elements), we build a temporary map of
479  // point identifier to ordinal, by looping through the sorted x/theta values. Since QMap is used, the x/theta keys are sorted
480  QMap<QString, double> pointIdentifierToOrdinal;
481  int ordinal = 0;
482  XOrThetaToPointIdentifier::const_iterator itrX;
483  for (itrX = xOrThetaToPointIdentifier.begin(); itrX != xOrThetaToPointIdentifier.end(); itrX++) {
484 
485  QString pointIdentifier = itrX.value();
486  pointIdentifierToOrdinal [pointIdentifier] = ordinal++;
487  }
488 
489  // Override the old ordinal values
490  for (itr = m_points.begin(); itr != m_points.end(); itr++) {
491  Point &point = *itr;
492  int ordinalNew = pointIdentifierToOrdinal [point.identifier()];
493  point.setOrdinal (ordinalNew);
494  }
495 }
496 
497 void Curve::updatePointOrdinalsRelations ()
498 {
499  CurveConnectAs curveConnectAs = m_curveStyle.lineStyle().curveConnectAs();
500 
501  LOG4CPP_INFO_S ((*mainCat)) << "Curve::updatePointOrdinalsRelations"
502  << " curve=" << m_curveName.toLatin1().data()
503  << " connectAs=" << curveConnectAsToString(curveConnectAs).toLatin1().data();
504 
505  // Keep the ordinal numbering, but make sure the ordinals are evenly spaced
506  Points::iterator itr;
507  int ordinal = 0;
508  for (itr = m_points.begin(); itr != m_points.end(); itr++) {
509  Point &point = *itr;
510  point.setOrdinal (ordinal++);
511  }
512 }
void transformScreenToRawGraph(const QPointF &coordScreen, QPointF &coordGraph) const
Transform from cartesian pixel screen coordinates to cartesian/polar graph coordinates.
void removePoint(const QString &identifier)
Perform the opposite of addPointAtEnd.
Definition: Curve.cpp:366
QPointF positionScreen(const QString &pointIdentifier) const
Return the position, in screen coordinates, of the specified Point.
Definition: Curve.cpp:329
QPointF posGraph(ApplyHasCheck applyHasCheck=KEEP_HAS_CHECK) const
Accessor for graph position. Skip check if copying one instance to another.
Definition: Point.cpp:333
Comparator for sorting Point class.
void saveXml(QXmlStreamWriter &writer, const QString &curveName) const
Serialize to xml.
Definition: CurveStyle.cpp:87
Color filter parameters for one curve. For a class, this is handled the same as LineStyle and PointSt...
void exportToClipboard(const QHash< QString, bool > &selectedHash, const Transformation &transformation, QTextStream &strCsv, QTextStream &strHtml, CurvesGraphs &curvesGraphs) const
Export points in this Curve found in the specified point list.
Definition: Curve.cpp:92
const Points points() const
Return a shallow copy of the Points.
Definition: Curve.cpp:307
void setCurveStyle(const CurveStyle &curveStyle)
Set curve style.
Definition: Curve.cpp:411
void printStream(QString indentation, QTextStream &str) const
Debugging method that supports print method of this class and printStream method of some other class(...
Definition: CurveStyle.cpp:74
void addPoint(Point point)
Add Point to this Curve.
Definition: Curve.cpp:55
void setColorFilterSettings(const ColorFilterSettings &colorFilterSettings)
Set color filter.
Definition: Curve.cpp:401
void saveXml(QXmlStreamWriter &writer) const
Serialize to stream.
Definition: Point.cpp:370
Curve * curveForCurveName(const QString &curveName)
Return the axis or graph curve for the specified curve name.
int numCurves() const
Current number of graphs curves.
int numPoints() const
Number of points.
Definition: Curve.cpp:288
void updatePointOrdinals(const Transformation &transformation)
See CurveGraphs::updatePointOrdinals.
Definition: Curve.cpp:416
LineStyle lineStyle() const
Get method for LineStyle.
Definition: CurveStyle.cpp:20
void addGraphCurveAtEnd(Curve curve)
Append new graph Curve to end of Curve list.
Class that represents one digitized point. The screen-to-graph coordinate transformation is always ex...
Definition: Point.h:17
static LineStyle defaultAxesCurve()
Initial default for axes curve.
Definition: LineStyle.cpp:48
QPointF posScreen() const
Accessor for screen position.
Definition: Point.cpp:342
QPointF positionGraph(const QString &pointIdentifier) const
Return the position, in graph coordinates, of the specified Point.
Definition: Curve.cpp:312
void setLineStyle(const LineStyle &lineStyle)
Set method for LineStyle.
Definition: CurveStyle.cpp:109
Curve(const QString &curveName, const ColorFilterSettings &colorFilterSettings, const CurveStyle &curveStyle)
Constructor from scratch.
Definition: Curve.cpp:23
void setPosGraph(const QPointF &posGraph)
Set method for position in graph coordinates.
Definition: Point.cpp:419
void printStream(QString indentation, QTextStream &str) const
Debugging method that supports print method of this class and printStream method of some other class(...
Definition: Curve.cpp:346
void editPoint(const QPointF &posGraph, const QString &identifier)
Edit the graph coordinates of an axis point. This method does not apply to a graph point...
Definition: Curve.cpp:75
Curve & operator=(const Curve &curve)
Assignment constructor.
Definition: Curve.cpp:45
CallbackSearchReturn
Return values for search callback methods.
QString identifier() const
Unique identifier for a specific Point.
Definition: Point.cpp:218
void movePoint(const QString &pointIdentifier, const QPointF &deltaScreen)
Translate the position of a point by the specified distance vector.
Definition: Curve.cpp:279
static ColorFilterSettings defaultFilter()
Initial default for any Curve.
Affine transformation between screen and graph coordinates, based on digitized axis points...
QString loadXml(QXmlStreamReader &reader)
Load from serialized xml. Returns the curve name.
Definition: CurveStyle.cpp:25
Container for all graph curves. The axes point curve is external to this class.
Definition: CurvesGraphs.h:18
void saveXml(QXmlStreamWriter &writer) const
Save curve filter to stream.
void iterateThroughCurvePoints(const Functor2wRet< const QString &, const Point &, CallbackSearchReturn > &ftorWithCallback) const
Apply functor to Points on Curve.
Definition: Curve.cpp:157
void printStream(QString indentation, QTextStream &str) const
Debugging method that supports print method of this class and printStream method of some other class(...
Definition: Point.cpp:347
void setCurveName(const QString &curveName)
Change the curve name.
Definition: Curve.cpp:406
Container for LineStyle and PointStyle for one Curve.
Definition: CurveStyle.h:12
void setPosScreen(const QPointF &posScreen)
Set method for position in screen coordinates.
Definition: Point.cpp:433
Container for one set of digitized Points.
Definition: Curve.h:24
bool transformIsDefined() const
Transform is defined when at least three axis points have been digitized.
void loadXml(QXmlStreamReader &reader)
Load curve filter to stream.
Immediately terminate the current search.
CurveStyle curveStyle() const
Return the curve style.
Definition: Curve.cpp:70
CurveConnectAs curveConnectAs() const
Get method for connect type.
Definition: LineStyle.cpp:43
void printStream(QString indentation, QTextStream &str) const
Debugging method that supports print method of this class and printStream method of some other class(...
void setPointStyle(const PointStyle &pointStyle)
Set method for PointStyle.
Definition: CurveStyle.cpp:139
void iterateThroughCurveSegments(const Functor2wRet< const Point &, const Point &, CallbackSearchReturn > &ftorWithCallback) const
Apply functor to successive Points, as line segments, on Curve. This could be a bit slow...
Definition: Curve.cpp:172
void saveXml(QXmlStreamWriter &writer) const
Serialize curve.
Definition: Curve.cpp:379
void setOrdinal(double ordinal)
Set the ordinal used for ordering Points.
Definition: Point.cpp:409
static PointStyle defaultGraphCurve(int index)
Initial default for index'th graph curve.
Definition: PointStyle.cpp:57
ColorFilterSettings colorFilterSettings() const
Return the color filter.
Definition: Curve.cpp:60
QString curveName() const
Name of this Curve.
Definition: Curve.cpp:65