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 "CurveStyle.h"
10 #include "DocumentSerialize.h"
11 #include "EngaugeAssert.h"
12 #include "Logger.h"
13 #include "MigrateToVersion6.h"
14 #include "Point.h"
15 #include "PointComparator.h"
16 #include <QDataStream>
17 #include <QDebug>
18 #include <qmath.h>
19 #include <QMultiMap>
20 #include <QTextStream>
21 #include <QXmlStreamReader>
22 #include <QXmlStreamWriter>
23 #include "Transformation.h"
24 #include "Xml.h"
25
26 const QString AXIS_CURVE_NAME ("Axes");
27 const QString DEFAULT_GRAPH_CURVE_NAME ("Curve1");
28 const QString DUMMY_CURVE_NAME ("dummy");
29 const QString SCALE_CURVE_NAME ("Scale"); // Used for pre-version 6 input files
30 const QString TAB_DELIMITER ("\t");
31
32 // This has to be a multimap instead of a map since some users may mistakenly allow multiple points with the
33 // same x coordinate in their functions even though that should not happen
34 typedef QMultiMap<double, QString> XOrThetaToPointIdentifier;
35
Curve(const QString & curveName,const ColorFilterSettings & colorFilterSettings,const CurveStyle & curveStyle)36 Curve::Curve(const QString &curveName,
37 const ColorFilterSettings &colorFilterSettings,
38 const CurveStyle &curveStyle) :
39 m_curveName (curveName),
40 m_colorFilterSettings (colorFilterSettings),
41 m_curveStyle (curveStyle)
42 {
43 }
44
Curve(const Curve & curve)45 Curve::Curve (const Curve &curve) :
46 m_curveName (curve.curveName ()),
47 m_points (curve.points ()),
48 m_colorFilterSettings (curve.colorFilterSettings ()),
49 m_curveStyle (curve.curveStyle ())
50 {
51 }
52
Curve(QDataStream & str)53 Curve::Curve (QDataStream &str)
54 {
55 const int CONVERT_ENUM_TO_RADIUS = 6;
56 MigrateToVersion6 migrate;
57
58 qint32 int32, xScreen, yScreen;
59 double xGraph, yGraph;
60
61 str >> m_curveName;
62
63 // Scale bar points are handled as if they are axis points
64 if (m_curveName == SCALE_CURVE_NAME) {
65 m_curveName = AXIS_CURVE_NAME;
66 }
67
68 str >> int32;
69 m_curveStyle.setPointShape(migrate.pointShape (int32));
70 str >> int32;
71 m_curveStyle.setPointRadius(int32 + CONVERT_ENUM_TO_RADIUS);
72 str >> int32;
73 m_curveStyle.setPointLineWidth (int32);
74 str >> int32;
75 m_curveStyle.setPointColor(migrate.colorPalette (int32));
76 str >> int32; // Point interior color
77 str >> int32;
78 m_curveStyle.setLineWidth(int32);
79 str >> int32;
80 if (m_curveName == AXIS_CURVE_NAME) {
81 m_curveStyle.setLineColor(migrate.colorPalette (int32));
82 } else {
83 m_curveStyle.setLineColor(COLOR_PALETTE_TRANSPARENT);
84 }
85 str >> int32;
86 m_curveStyle.setLineConnectAs(migrate.curveConnectAs (int32));
87
88 str >> int32;
89 int count = int32;
90 int ordinal = 0;
91 for (int i = 0; i < count; i++) {
92
93 str >> xScreen;
94 str >> yScreen;
95 str >> xGraph;
96 str >> yGraph;
97 if (m_curveName == AXIS_CURVE_NAME) {
98
99 // Axis point, with graph coordinates set by user and managed here
100 Point point (m_curveName,
101 QPointF (xScreen, yScreen),
102 QPointF (xGraph, yGraph),
103 ordinal++,
104 false);
105
106 addPoint(point);
107 } else {
108
109 // Curve point, with graph coordinates managed elsewhere
110 Point point (m_curveName,
111 QPointF (xScreen, yScreen));
112 point.setOrdinal (ordinal++);
113
114 addPoint(point);
115 }
116 }
117 }
118
Curve(QXmlStreamReader & reader)119 Curve::Curve (QXmlStreamReader &reader)
120 {
121 loadXml(reader);
122 }
123
operator =(const Curve & curve)124 Curve &Curve::operator=(const Curve &curve)
125 {
126 m_curveName = curve.curveName ();
127 m_points = curve.points ();
128 m_colorFilterSettings = curve.colorFilterSettings ();
129 m_curveStyle = curve.curveStyle ();
130
131 return *this;
132 }
133
addPoint(const Point & point)134 void Curve::addPoint (const Point &point)
135 {
136 m_points.push_back (point);
137 }
138
colorFilterSettings() const139 ColorFilterSettings Curve::colorFilterSettings () const
140 {
141 return m_colorFilterSettings;
142 }
143
curveName() const144 QString Curve::curveName () const
145 {
146 return m_curveName;
147 }
148
curveStyle() const149 CurveStyle Curve::curveStyle() const
150 {
151 return m_curveStyle;
152 }
153
editPointAxis(const QPointF & posGraph,const QString & identifier)154 void Curve::editPointAxis (const QPointF &posGraph,
155 const QString &identifier)
156 {
157 // Search for the point with matching identifier
158 QList<Point>::iterator itr;
159 for (itr = m_points.begin (); itr != m_points.end (); itr++) {
160
161 Point &point = *itr;
162 if (point.identifier () == identifier) {
163
164 point.setPosGraph (posGraph);
165 break;
166
167 }
168 }
169 }
170
editPointGraph(bool isX,bool isY,double x,double y,const QStringList & identifiers,const Transformation & transformation)171 void Curve::editPointGraph (bool isX,
172 bool isY,
173 double x,
174 double y,
175 const QStringList &identifiers,
176 const Transformation &transformation)
177 {
178 LOG4CPP_INFO_S ((*mainCat)) << "Curve::editPointGraph"
179 << " identifiers=" << identifiers.join(" ").toLatin1().data();
180
181 if (transformation.transformIsDefined()) {
182
183 // Search for the point with matching identifier
184 QList<Point>::iterator itr;
185 for (itr = m_points.begin(); itr != m_points.end(); itr++) {
186
187 Point &point = *itr;
188
189 if (identifiers.contains (point.identifier ())) {
190
191 // Although one or more graph coordinates are specified, it is the screen coordinates that must be
192 // moved. This is because only the screen coordinates of the graph points are tracked (not the graph coordinates).
193 // So we compute posScreen and call Point::setPosScreen instead of Point::setPosGraph
194
195 // Get original graph coordinates
196 QPointF posScreen = point.posScreen ();
197 QPointF posGraph;
198 transformation.transformScreenToRawGraph (posScreen,
199 posGraph);
200
201 // Override one or both coordinates
202 if (isX) {
203 posGraph.setX (x);
204 }
205
206 if (isY) {
207 posGraph.setY (y);
208 }
209
210 // Set the screen coordinates
211 transformation.transformRawGraphToScreen(posGraph,
212 posScreen);
213
214 point.setPosScreen (posScreen);
215 }
216 }
217 }
218 }
219
exportToClipboard(const QHash<QString,bool> & selectedHash,const Transformation & transformation,QTextStream & strCsv,QTextStream & strHtml,CurvesGraphs & curvesGraphs) const220 void Curve::exportToClipboard (const QHash<QString, bool> &selectedHash,
221 const Transformation &transformation,
222 QTextStream &strCsv,
223 QTextStream &strHtml,
224 CurvesGraphs &curvesGraphs) const
225 {
226 LOG4CPP_INFO_S ((*mainCat)) << "Curve::exportToClipboard"
227 << " hashCount=" << selectedHash.count();
228
229 // This method assumes Copy is only allowed when Transformation is valid
230
231 bool isFirst = true;
232 QList<Point>::const_iterator itr;
233 for (itr = m_points.begin (); itr != m_points.end (); itr++) {
234
235 const Point &point = *itr;
236 if (selectedHash.contains (point.identifier ())) {
237
238 if (isFirst) {
239
240 // Insert headers to identify the points that follow
241 isFirst = false;
242 strCsv << "X" << TAB_DELIMITER << m_curveName << "\n";
243 strHtml << "<table>\n"
244 << "<tr><th>X</th><th>" << m_curveName << "</th></tr>\n";
245 }
246
247 // Default curve style
248 CurveStyle curveStyleDefault;
249 curveStyleDefault.setLineStyle(LineStyle::defaultAxesCurve());
250 curveStyleDefault.setPointStyle(PointStyle::defaultGraphCurve (curvesGraphs.numCurves ()));
251
252 // Check if this curve already exists from a previously exported point
253 if (curvesGraphs.curveForCurveName (m_curveName) == nullptr) {
254 Curve curve(m_curveName,
255 ColorFilterSettings::defaultFilter (),
256 curveStyleDefault);
257 curvesGraphs.addGraphCurveAtEnd(curve);
258 }
259
260 // Start with screen coordinates
261 QPointF pos = point.posScreen();
262 if (transformation.transformIsDefined()) {
263
264 // Replace with graph coordinates which are almost always more useful
265 QPointF posGraph;
266 transformation.transformScreenToRawGraph(pos,
267 posGraph);
268 pos = posGraph;
269 }
270
271 // Add point to text going to clipboard
272 strCsv << pos.x() << TAB_DELIMITER << pos.y() << "\n";
273 strHtml << "<tr><td>" << pos.x() << "</td><td>" << pos.y() << "</td></tr>\n";
274
275 // Add point to list for undo/redo
276 curvesGraphs.curveForCurveName (m_curveName)->addPoint (point);
277 }
278 }
279
280 if (!isFirst) {
281 strHtml << "</table>\n";
282 }
283 }
284
isXOnly(const QString & pointIdentifier) const285 bool Curve::isXOnly(const QString &pointIdentifier) const
286 {
287 // Search for point with matching identifier
288 Points::const_iterator itr;
289 for (itr = m_points.begin (); itr != m_points.end (); itr++) {
290 const Point &point = *itr;
291 if (pointIdentifier == point.identifier ()) {
292 return point.isXOnly();
293 }
294 }
295
296 LOG4CPP_ERROR_S ((*mainCat)) << "Curve::isXOnly encountered unknown indentifier "
297 << pointIdentifier.toLatin1().data();
298 ENGAUGE_ASSERT (false);
299
300 return false;
301 }
302
iterateThroughCurvePoints(const Functor2wRet<const QString &,const Point &,CallbackSearchReturn> & ftorWithCallback) const303 void Curve::iterateThroughCurvePoints (const Functor2wRet<const QString &, const Point&, CallbackSearchReturn> &ftorWithCallback) const
304 {
305 QList<Point>::const_iterator itr;
306 for (itr = m_points.begin (); itr != m_points.end (); itr++) {
307
308 const Point &point = *itr;
309
310 CallbackSearchReturn rtn = ftorWithCallback (m_curveName, point);
311
312 if (rtn == CALLBACK_SEARCH_RETURN_INTERRUPT) {
313 break;
314 }
315 }
316 }
317
iterateThroughCurveSegments(const Functor2wRet<const Point &,const Point &,CallbackSearchReturn> & ftorWithCallback) const318 void Curve::iterateThroughCurveSegments (const Functor2wRet<const Point&, const Point&, CallbackSearchReturn> &ftorWithCallback) const
319 {
320 // Loop through Points. They are assumed to be already sorted by their ordinals, but we do NOT
321 // check the ordinal ordering since this could be called before, or while, the ordinal sorting is done
322 QList<Point>::const_iterator itr;
323 const Point *pointBefore = nullptr;
324 for (itr = m_points.begin(); itr != m_points.end(); itr++) {
325
326 const Point &point = *itr;
327
328 if (pointBefore != nullptr) {
329
330 CallbackSearchReturn rtn = ftorWithCallback (*pointBefore,
331 point);
332
333 if (rtn == CALLBACK_SEARCH_RETURN_INTERRUPT) {
334 break;
335 }
336
337 }
338 pointBefore = &point;
339 }
340 }
341
loadCurvePoints(QXmlStreamReader & reader)342 void Curve::loadCurvePoints(QXmlStreamReader &reader)
343 {
344 LOG4CPP_INFO_S ((*mainCat)) << "Curve::loadCurvePoints";
345
346 bool success = true;
347
348 while ((reader.tokenType() != QXmlStreamReader::EndElement) ||
349 (reader.name() != DOCUMENT_SERIALIZE_CURVE_POINTS)) {
350
351 QXmlStreamReader::TokenType tokenType = loadNextFromReader(reader);
352
353 if (reader.atEnd()) {
354 success = false;
355 break;
356 }
357
358 if (tokenType == QXmlStreamReader::StartElement) {
359
360 if (reader.name () == DOCUMENT_SERIALIZE_POINT) {
361
362 Point point (reader);
363 m_points.push_back (point);
364 }
365 }
366 }
367
368 if (!success) {
369 reader.raiseError(QObject::tr ("Cannot read curve data"));
370 }
371 }
372
loadXml(QXmlStreamReader & reader)373 void Curve::loadXml(QXmlStreamReader &reader)
374 {
375 LOG4CPP_INFO_S ((*mainCat)) << "Curve::loadXml";
376
377 bool success = true;
378
379 QXmlStreamAttributes attributes = reader.attributes();
380
381 if (attributes.hasAttribute (DOCUMENT_SERIALIZE_CURVE_NAME)) {
382
383 setCurveName (attributes.value (DOCUMENT_SERIALIZE_CURVE_NAME).toString());
384
385 // Read until end of this subtree
386 while ((reader.tokenType() != QXmlStreamReader::EndElement) ||
387 (reader.name() != DOCUMENT_SERIALIZE_CURVE)){
388
389 QXmlStreamReader::TokenType tokenType = loadNextFromReader(reader);
390
391 if (reader.atEnd()) {
392 success = false;
393 break;
394 }
395
396 if (tokenType == QXmlStreamReader::StartElement) {
397
398 if (reader.name() == DOCUMENT_SERIALIZE_COLOR_FILTER) {
399 m_colorFilterSettings.loadXml(reader);
400 } else if (reader.name() == DOCUMENT_SERIALIZE_CURVE_POINTS) {
401 loadCurvePoints(reader);
402 } else if (reader.name() == DOCUMENT_SERIALIZE_CURVE_STYLE) {
403 m_curveStyle.loadXml(reader);
404 } else {
405 success = false;
406 break;
407 }
408 }
409
410 if (reader.hasError()) {
411 // No need to set success flag to indicate failure, which raises the error, since the error was already raised. Just
412 // need to exit the loop immediately
413 break;
414 }
415 }
416 } else {
417 success = false;
418 }
419
420 if (!success) {
421 reader.raiseError (QObject::tr ("Cannot read curve data"));
422 }
423 }
424
movePoint(const QString & pointIdentifier,const QPointF & deltaScreen)425 void Curve::movePoint (const QString &pointIdentifier,
426 const QPointF &deltaScreen)
427 {
428 Point *point = pointForPointIdentifier (pointIdentifier);
429
430 QPointF posScreen = deltaScreen + point->posScreen ();
431 point->setPosScreen (posScreen);
432 }
433
numPoints() const434 int Curve::numPoints () const
435 {
436 return m_points.count ();
437 }
438
pointForPointIdentifier(const QString pointIdentifier)439 Point *Curve::pointForPointIdentifier (const QString pointIdentifier)
440 {
441 Points::iterator itr;
442 for (itr = m_points.begin (); itr != m_points.end (); itr++) {
443 Point &point = *itr;
444 if (pointIdentifier == point.identifier ()) {
445 return &point;
446 }
447 }
448
449 LOG4CPP_ERROR_S ((*mainCat)) << "Curve::pointForPointIdentifier encountered unknown indentifier "
450 << pointIdentifier.toLatin1().data();
451 ENGAUGE_ASSERT (false);
452 return nullptr;
453 }
454
points() const455 const Points Curve::points () const
456 {
457 return m_points;
458 }
459
positionGraph(const QString & pointIdentifier) const460 QPointF Curve::positionGraph (const QString &pointIdentifier) const
461 {
462 QPointF posGraph;
463
464 // Search for point with matching identifier
465 Points::const_iterator itr;
466 for (itr = m_points.begin (); itr != m_points.end (); itr++) {
467 const Point &point = *itr;
468 if (pointIdentifier == point.identifier ()) {
469 posGraph = point.posGraph ();
470 break;
471 }
472 }
473
474 return posGraph;
475 }
476
positionScreen(const QString & pointIdentifier) const477 QPointF Curve::positionScreen (const QString &pointIdentifier) const
478 {
479 QPointF posScreen;
480
481 // Search for point with matching identifier
482 Points::const_iterator itr;
483 for (itr = m_points.begin (); itr != m_points.end (); itr++) {
484 const Point &point = *itr;
485 if (pointIdentifier == point.identifier ()) {
486 posScreen = point.posScreen ();
487 break;
488 }
489 }
490
491 return posScreen;
492 }
493
printStream(QString indentation,QTextStream & str) const494 void Curve::printStream (QString indentation,
495 QTextStream &str) const
496 {
497 str << indentation << "Curve=" << m_curveName << "\n";
498
499 indentation += INDENTATION_DELTA;
500
501 Points::const_iterator itr;
502 for (itr = m_points.begin (); itr != m_points.end (); itr++) {
503 const Point &point = *itr;
504 point.printStream (indentation,
505 str);
506 }
507
508 m_colorFilterSettings.printStream (indentation,
509 str);
510 m_curveStyle.printStream (indentation,
511 str);
512 }
513
removePoint(const QString & identifier)514 void Curve::removePoint (const QString &identifier)
515 {
516 // Search for point with matching identifier
517 Points::iterator itr;
518 for (itr = m_points.begin (); itr != m_points.end (); itr++) {
519 Point point = *itr;
520 if (point.identifier () == identifier) {
521 m_points.erase (itr);
522 break;
523 }
524 }
525 }
526
saveXml(QXmlStreamWriter & writer) const527 void Curve::saveXml(QXmlStreamWriter &writer) const
528 {
529 LOG4CPP_INFO_S ((*mainCat)) << "Curve::saveXml";
530
531 writer.writeStartElement(DOCUMENT_SERIALIZE_CURVE);
532 writer.writeAttribute(DOCUMENT_SERIALIZE_CURVE_NAME, m_curveName);
533 m_colorFilterSettings.saveXml (writer,
534 m_curveName);
535 m_curveStyle.saveXml (writer,
536 m_curveName);
537
538 // Loop through points
539 writer.writeStartElement(DOCUMENT_SERIALIZE_CURVE_POINTS);
540 Points::const_iterator itr;
541 for (itr = m_points.begin (); itr != m_points.end (); itr++) {
542 const Point &point = *itr;
543 point.saveXml (writer);
544 }
545 writer.writeEndElement();
546
547 writer.writeEndElement();
548 }
549
setColorFilterSettings(const ColorFilterSettings & colorFilterSettings)550 void Curve::setColorFilterSettings (const ColorFilterSettings &colorFilterSettings)
551 {
552 m_colorFilterSettings = colorFilterSettings;
553 }
554
setCurveName(const QString & curveName)555 void Curve::setCurveName (const QString &curveName)
556 {
557 m_curveName = curveName;
558
559 // Pass to member objects
560 QList<Point>::iterator itr;
561 for (itr = m_points.begin(); itr != m_points.end(); itr++) {
562 Point &point = *itr;
563 point.setCurveName (curveName);
564 }
565 }
566
setCurveStyle(const CurveStyle & curveStyle)567 void Curve::setCurveStyle (const CurveStyle &curveStyle)
568 {
569 m_curveStyle = curveStyle;
570 }
571
updatePointOrdinals(const Transformation & transformation)572 void Curve::updatePointOrdinals (const Transformation &transformation)
573 {
574 CurveConnectAs curveConnectAs = m_curveStyle.lineStyle().curveConnectAs();
575
576 LOG4CPP_INFO_S ((*mainCat)) << "Curve::updatePointOrdinals"
577 << " curve=" << m_curveName.toLatin1().data()
578 << " connectAs=" << curveConnectAsToString(curveConnectAs).toLatin1().data();
579
580 // Make sure ordinals are properly ordered. Sorting is done afterward
581
582 if (curveConnectAs == CONNECT_AS_FUNCTION_SMOOTH ||
583 curveConnectAs == CONNECT_AS_FUNCTION_STRAIGHT) {
584
585 updatePointOrdinalsFunctions (transformation);
586
587 } else if (curveConnectAs == CONNECT_AS_RELATION_SMOOTH ||
588 curveConnectAs == CONNECT_AS_RELATION_STRAIGHT) {
589
590 updatePointOrdinalsRelations ();
591
592 } else {
593
594 LOG4CPP_ERROR_S ((*mainCat)) << "Curve::updatePointOrdinals encountered unexpected connection configuration";
595 ENGAUGE_ASSERT (false);
596
597 }
598
599 std::sort (m_points.begin(),
600 m_points.end(),
601 PointComparator());
602 }
603
updatePointOrdinalsFunctions(const Transformation & transformation)604 void Curve::updatePointOrdinalsFunctions (const Transformation &transformation)
605 {
606 CurveConnectAs curveConnectAs = m_curveStyle.lineStyle().curveConnectAs();
607
608 LOG4CPP_INFO_S ((*mainCat)) << "Curve::updatePointOrdinalsFunctions"
609 << " curve=" << m_curveName.toLatin1().data()
610 << " connectAs=" << curveConnectAsToString(curveConnectAs).toLatin1().data();
611
612 // Get a map of x/theta values as keys with point identifiers as the values. This has to be a multimap since
613 // some users will have two (or maybe more) points with the same x coordinate, even though true functions should
614 // never have that happen
615 XOrThetaToPointIdentifier xOrThetaToPointIdentifier;
616 Points::iterator itr;
617 for (itr = m_points.begin (); itr != m_points.end (); itr++) {
618 Point &point = *itr;
619
620 QPointF posGraph;
621 if (transformation.transformIsDefined()) {
622
623 // Transformation is available so use it
624 transformation.transformScreenToRawGraph (point.posScreen (),
625 posGraph);
626 } else {
627
628 // Transformation is not available so we just use the screen coordinates. Effectively, the
629 // transformation is the identity matrix
630 posGraph= point.posScreen();
631 }
632
633 xOrThetaToPointIdentifier.insert (posGraph.x(),
634 point.identifier());
635 }
636
637 // Every point in m_points must be in the map. Failure to perform this check will probably result in the assert
638 // below getting triggered
639 ENGAUGE_ASSERT (xOrThetaToPointIdentifier.count () == m_points.count ());
640
641 // Since m_points is a list (and therefore does not provide direct access to elements), we build a temporary map of
642 // point identifier to ordinal, by looping through the sorted x/theta values. Since QMap is used, the x/theta keys are sorted
643 QMap<QString, double> pointIdentifierToOrdinal;
644 int ordinal = 0;
645 XOrThetaToPointIdentifier::const_iterator itrX;
646 for (itrX = xOrThetaToPointIdentifier.begin(); itrX != xOrThetaToPointIdentifier.end(); itrX++) {
647
648 QString pointIdentifier = itrX.value();
649 pointIdentifierToOrdinal [pointIdentifier] = ordinal++;
650 }
651
652 // Override the old ordinal values
653 for (itr = m_points.begin(); itr != m_points.end(); itr++) {
654 Point &point = *itr;
655
656 // Make sure point is in the map list. If this test is skipped then the square bracket operator
657 // will insert an entry with a zero ordinal, and the presence of multiple points with the same zero ordinal will
658 // cause problems downstream
659 ENGAUGE_ASSERT (pointIdentifierToOrdinal.contains (point.identifier()));
660
661 // Point is to be included since it is in the map list.
662 int ordinalNew = qFloor (pointIdentifierToOrdinal [point.identifier()]);
663 point.setOrdinal (ordinalNew);
664 }
665 }
666
updatePointOrdinalsRelations()667 void Curve::updatePointOrdinalsRelations ()
668 {
669 CurveConnectAs curveConnectAs = m_curveStyle.lineStyle().curveConnectAs();
670
671 LOG4CPP_INFO_S ((*mainCat)) << "Curve::updatePointOrdinalsRelations"
672 << " curve=" << m_curveName.toLatin1().data()
673 << " connectAs=" << curveConnectAsToString(curveConnectAs).toLatin1().data();
674
675 // Keep the ordinal numbering, but make sure the ordinals are evenly spaced
676 Points::iterator itr;
677 int ordinal = 0;
678 for (itr = m_points.begin(); itr != m_points.end(); itr++) {
679 Point &point = *itr;
680 point.setOrdinal (ordinal++);
681 }
682 }
683