Engauge Digitizer 2
Loading...
Searching...
No Matches
Point.cpp
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 "DocumentSerialize.h"
8#include "EngaugeAssert.h"
9#include "Logger.h"
10#include "Point.h"
11#include <QObject>
12#include <QStringList>
13#include <QTextStream>
14#include "QtToString.h"
15#include <QXmlStreamReader>
16#include <QXmlStreamWriter>
17#include "Xml.h"
18
19unsigned int Point::m_identifierIndex = 0;
20
21extern const QString AXIS_CURVE_NAME;
22extern const QString DUMMY_CURVE_NAME;
23const QString POINT_IDENTIFIER_DELIMITER_SAFE ("\t"); // Character that could never be entered when editing curve names
24const QString POINT_IDENTIFIER_DELIMITER_XML ("_"); // From incoming xml that does not like tabs
25
26const double MISSING_ORDINAL_VALUE = 0;
27const double MISSING_POSGRAPH_VALUE = 0;
28
30{
31}
32
33Point::Point(const QString &curveName,
34 const QPointF &posScreen) :
35 m_isAxisPoint (curveName == AXIS_CURVE_NAME),
36 m_identifier (uniqueIdentifierGenerator(curveName)),
37 m_posScreen (posScreen),
38 m_hasPosGraph (false),
39 m_posGraph (MISSING_POSGRAPH_VALUE, MISSING_POSGRAPH_VALUE),
40 m_hasOrdinal (false),
41 m_ordinal (MISSING_ORDINAL_VALUE),
42 m_isXOnly (false)
43{
44 LOG4CPP_INFO_S ((*mainCat)) << "Point::Point"
45 << " curveName=" << curveName.toLatin1().data()
46 << " identifierGenerated=" << m_identifier.toLatin1().data()
47 << " posScreen=" << QPointFToString (posScreen).toLatin1().data();
48
49 ENGAUGE_ASSERT (!curveName.isEmpty ());
50}
51
52Point::Point(const QString &curveName,
53 const QPointF &posScreen,
54 const QPointF &posGraph,
55 bool isXOnly) :
56 m_isAxisPoint (true),
57 m_identifier (uniqueIdentifierGenerator(curveName)),
58 m_posScreen (posScreen),
59 m_hasPosGraph (true),
60 m_posGraph (posGraph),
61 m_hasOrdinal (false),
62 m_ordinal (MISSING_ORDINAL_VALUE),
63 m_isXOnly (isXOnly)
64{
65 ENGAUGE_ASSERT (curveName == AXIS_CURVE_NAME ||
66 curveName == DUMMY_CURVE_NAME);
67
68 LOG4CPP_INFO_S ((*mainCat)) << "Point::Point"
69 << " curveName=" << curveName.toLatin1().data()
70 << " identifierGenerated=" << m_identifier.toLatin1().data()
71 << " posScreen=" << QPointFToString (posScreen).toLatin1().data()
72 << " posGraph=" << QPointFToString (posGraph).toLatin1().data()
73 << " isXOnly=" << (isXOnly ? "true" : "false");
74
75 ENGAUGE_ASSERT (!curveName.isEmpty ());
76}
77
78Point::Point(const QString &curveName,
79 const QString &identifier,
80 const QPointF &posScreen,
81 const QPointF &posGraph,
82 double ordinal,
83 bool isXOnly) :
84 m_isAxisPoint (true),
85 m_identifier (identifier),
86 m_posScreen (posScreen),
87 m_hasPosGraph (true),
88 m_posGraph (posGraph),
89 m_hasOrdinal (true),
90 m_ordinal (ordinal),
91 m_isXOnly (isXOnly)
92{
93 ENGAUGE_ASSERT (curveName == AXIS_CURVE_NAME);
94
95 LOG4CPP_INFO_S ((*mainCat)) << "Point::Point"
96 << " curveName=" << curveName.toLatin1().data()
97 << " identifier=" << m_identifier.toLatin1().data()
98 << " posScreen=" << QPointFToString (posScreen).toLatin1().data()
99 << " posGraph=" << QPointFToString (posGraph).toLatin1().data()
100 << " ordinal=" << ordinal
101 << " isXOnly=" << (isXOnly ? "true" : "false");
102
103 ENGAUGE_ASSERT (!curveName.isEmpty ());
104}
105
106Point::Point(const QString &curveName,
107 const QPointF &posScreen,
108 const QPointF &posGraph,
109 double ordinal,
110 bool isXOnly) :
111 m_isAxisPoint (true),
112 m_identifier (uniqueIdentifierGenerator(curveName)),
113 m_posScreen (posScreen),
114 m_hasPosGraph (true),
115 m_posGraph (posGraph),
116 m_hasOrdinal (true),
117 m_ordinal (ordinal),
118 m_isXOnly (isXOnly)
119{
120 ENGAUGE_ASSERT (curveName == AXIS_CURVE_NAME);
121
122 LOG4CPP_INFO_S ((*mainCat)) << "Point::Point"
123 << " curveName=" << curveName.toLatin1().data()
124 << " identifierGenerated=" << m_identifier.toLatin1().data()
125 << " posScreen=" << QPointFToString (posScreen).toLatin1().data()
126 << " posGraph=" << QPointFToString (posGraph).toLatin1().data()
127 << " ordinal=" << ordinal
128 << " isXOnly=" << (isXOnly ? "true" : "false");
129
130 ENGAUGE_ASSERT (!curveName.isEmpty ());
131}
132
133Point::Point(const QString &curveName,
134 const QString &identifier,
135 const QPointF &posScreen,
136 double ordinal) :
137 m_isAxisPoint (false),
138 m_identifier (identifier),
139 m_posScreen (posScreen),
140 m_hasPosGraph (false),
141 m_posGraph (MISSING_POSGRAPH_VALUE, MISSING_POSGRAPH_VALUE),
142 m_hasOrdinal (true),
143 m_ordinal (ordinal),
144 m_isXOnly (false)
145{
146 ENGAUGE_ASSERT (curveName != AXIS_CURVE_NAME);
147
148 LOG4CPP_INFO_S ((*mainCat)) << "Point::Point"
149 << " curveName=" << curveName.toLatin1().data()
150 << " identifier=" << identifier.toLatin1().data()
151 << " posScreen=" << QPointFToString (posScreen).toLatin1().data()
152 << " ordinal=" << ordinal;
153
154 ENGAUGE_ASSERT (!curveName.isEmpty ());
155}
156
157Point::Point (const QString &curveName,
158 const QPointF &posScreen,
159 double ordinal) :
160 m_isAxisPoint (false),
161 m_identifier (uniqueIdentifierGenerator(curveName)),
162 m_posScreen (posScreen),
163 m_hasPosGraph (false),
164 m_posGraph (MISSING_POSGRAPH_VALUE, MISSING_POSGRAPH_VALUE),
165 m_hasOrdinal (true),
166 m_ordinal (ordinal),
167 m_isXOnly (false)
168{
169 ENGAUGE_ASSERT (curveName != AXIS_CURVE_NAME);
170
171 LOG4CPP_INFO_S ((*mainCat)) << "Point::Point(identifier,posScreen,posGraph,ordinal)"
172 << " identifierGenerated=" << m_identifier.toLatin1().data()
173 << " posScreen=" << QPointFToString (posScreen).toLatin1().data()
174 << " ordinal=" << ordinal;
175}
176
177Point::Point (QXmlStreamReader &reader)
178{
179 loadXml(reader);
180}
181
182Point::Point (const Point &other)
183{
184 LOG4CPP_INFO_S ((*mainCat)) << "Point::Point(const Point &other)"
185 << " isAxisPoint=" << (other.isAxisPoint() ? "true" : "false")
186 << " identifier=" << other.identifier ().toLatin1().data()
187 << " posScreen=" << QPointFToString (other.posScreen ()).toLatin1().data()
188 << " hasPosGraph=" << (other.hasPosGraph() ? "true" : "false")
189 << " posGraph=" << QPointFToString (other.posGraph (SKIP_HAS_CHECK)).toLatin1().data()
190 << " hasOrdinal=" << (other.hasOrdinal() ? "true" : "false")
191 << " ordinal=" << other.ordinal (SKIP_HAS_CHECK)
192 << " isXOnly=" << other.isXOnly ();
193
194 m_isAxisPoint = other.isAxisPoint ();
195 m_identifier = other.identifier ();
196 m_posScreen = other.posScreen ();
197 m_hasPosGraph = other.hasPosGraph ();
198 m_posGraph = other.posGraph (SKIP_HAS_CHECK);
199 m_hasOrdinal = other.hasOrdinal ();
200 m_ordinal = other.ordinal (SKIP_HAS_CHECK);
201 m_isXOnly = other.isXOnly ();
202}
203
205{
206 LOG4CPP_DEBUG_S ((*mainCat)) << "Point::operator="
207 << " isAxisPoint=" << (point.isAxisPoint() ? "true" : "false")
208 << " identifier=" << point.identifier ().toLatin1().data()
209 << " posScreen=" << QPointFToString (point.posScreen ()).toLatin1().data()
210 << " hasPosGraph=" << (point.hasPosGraph() ? "true" : "false")
211 << " posGraph=" << QPointFToString (point.posGraph (SKIP_HAS_CHECK)).toLatin1().data()
212 << " hasOrdinal=" << (point.hasOrdinal() ? "true" : "false")
213 << " ordinal=" << point.ordinal (SKIP_HAS_CHECK);
214
215 m_isAxisPoint = point.isAxisPoint ();
216 m_identifier = point.identifier ();
217 m_posScreen = point.posScreen ();
218 m_hasPosGraph = point.hasPosGraph ();
219 m_posGraph = point.posGraph (SKIP_HAS_CHECK);
220 m_hasOrdinal = point.hasOrdinal ();
221 m_ordinal = point.ordinal (SKIP_HAS_CHECK);
222 m_isXOnly = point.isXOnly ();
223
224 return *this;
225}
226
227QString Point::curveNameFromPointIdentifier (const QString &pointIdentifier)
228{
229 QStringList tokens;
230
231 if (pointIdentifier.contains (POINT_IDENTIFIER_DELIMITER_SAFE)) {
232
233 tokens = pointIdentifier.split (POINT_IDENTIFIER_DELIMITER_SAFE);
234
235 } else {
236
237 // Yes, this is a hack - underscores could have been inserted by user (in the curve name) and/or this source code,
238 // but there are many dig files laying around that have underscores so we need to support them
239 tokens = pointIdentifier.split (POINT_IDENTIFIER_DELIMITER_XML);
240
241 }
242
243 return tokens.value (0);
244}
245
246bool Point::hasOrdinal () const
247{
248 return m_hasOrdinal;
249}
250
252{
253 return m_hasPosGraph;
254}
255
256QString Point::identifier() const
257{
258 return m_identifier;
259}
260
262{
263 LOG4CPP_INFO_S ((*mainCat)) << "Point::identifierIndex"
264 << " identifierIndex=" << m_identifierIndex;
265
266 return m_identifierIndex;
267}
268
270{
271 return m_isAxisPoint;
272}
273
274bool Point::isXOnly() const
275{
276 return m_isXOnly;
277}
278
279void Point::loadXml(QXmlStreamReader &reader)
280{
281 LOG4CPP_INFO_S ((*mainCat)) << "Point::loadXml";
282
283 bool success = true;
284
285 QXmlStreamAttributes attributes = reader.attributes();
286
287 // Note that DOCUMENT_SERIALIZE_POINT_IS_X_ONLY is optional since it is not used in Version 6
288 // but is used in Version 7
289 if (attributes.hasAttribute(DOCUMENT_SERIALIZE_POINT_IDENTIFIER) &&
290 attributes.hasAttribute(DOCUMENT_SERIALIZE_POINT_IDENTIFIER_INDEX) &&
291 attributes.hasAttribute(DOCUMENT_SERIALIZE_POINT_IS_AXIS_POINT)) {
292
293 m_hasOrdinal = attributes.hasAttribute(DOCUMENT_SERIALIZE_POINT_ORDINAL);
294 if (m_hasOrdinal) {
295 m_ordinal = attributes.value(DOCUMENT_SERIALIZE_POINT_ORDINAL).toDouble();
296 } else {
297 m_ordinal = MISSING_ORDINAL_VALUE;
298 }
299
300 QString isAxisPoint = attributes.value(DOCUMENT_SERIALIZE_POINT_IS_AXIS_POINT).toString();
301 QString isXOnly; // Default is anything but DOCUMENT_SERIALIZE_BOOL_TRUE
302 if (attributes.hasAttribute (DOCUMENT_SERIALIZE_POINT_IS_X_ONLY)) {
303 isXOnly = attributes.value(DOCUMENT_SERIALIZE_POINT_IS_X_ONLY).toString();
304 }
305
306 m_identifier = attributes.value(DOCUMENT_SERIALIZE_POINT_IDENTIFIER).toString();
307 m_identifierIndex = attributes.value(DOCUMENT_SERIALIZE_POINT_IDENTIFIER_INDEX).toInt();
308 m_isAxisPoint = (isAxisPoint == DOCUMENT_SERIALIZE_BOOL_TRUE);
309 m_hasPosGraph = false;
310 m_posGraph.setX (MISSING_POSGRAPH_VALUE);
311 m_posGraph.setY (MISSING_POSGRAPH_VALUE);
312 m_isXOnly = (isXOnly == DOCUMENT_SERIALIZE_BOOL_TRUE);
313
314 while ((reader.tokenType() != QXmlStreamReader::EndElement) ||
315 (reader.name () != DOCUMENT_SERIALIZE_POINT)) {
316
317 loadNextFromReader(reader);
318 if (reader.atEnd()) {
319 success = false;
320 break;
321 }
322
323 if (reader.tokenType () == QXmlStreamReader::StartElement) {
324
325 if (reader.name() == DOCUMENT_SERIALIZE_POINT_POSITION_SCREEN) {
326
327 attributes = reader.attributes();
328
329 if (attributes.hasAttribute(DOCUMENT_SERIALIZE_POINT_X) &&
330 attributes.hasAttribute(DOCUMENT_SERIALIZE_POINT_Y)) {
331
332 m_posScreen.setX (attributes.value(DOCUMENT_SERIALIZE_POINT_X).toDouble());
333 m_posScreen.setY (attributes.value(DOCUMENT_SERIALIZE_POINT_Y).toDouble());
334
335 } else {
336 success = false;
337 break;
338 }
339 } else if (reader.name() == DOCUMENT_SERIALIZE_POINT_POSITION_GRAPH) {
340
341 m_hasPosGraph = true;
342 attributes = reader.attributes();
343
344 if (attributes.hasAttribute(DOCUMENT_SERIALIZE_POINT_X) &&
345 attributes.hasAttribute(DOCUMENT_SERIALIZE_POINT_Y)) {
346
347 m_posGraph.setX (attributes.value(DOCUMENT_SERIALIZE_POINT_X).toDouble());
348 m_posGraph.setY (attributes.value(DOCUMENT_SERIALIZE_POINT_Y).toDouble());
349
350 } else {
351 success = false;
352 break;
353 }
354 }
355 }
356 }
357
358 LOG4CPP_INFO_S ((*mainCat)) << "Point::loadXml"
359 << " identifier=" << m_identifier.toLatin1().data()
360 << " identifierIndex=" << m_identifierIndex
361 << " posScreen=" << QPointFToString (m_posScreen).toLatin1().data()
362 << " posGraph=" << QPointFToString (m_posGraph).toLatin1().data()
363 << " ordinal=" << m_ordinal;
364
365 } else {
366 success = false;
367 }
368
369 if (!success) {
370 reader.raiseError(QObject::tr ("Cannot read point data"));
371 }
372}
373
374double Point::ordinal (ApplyHasCheck applyHasCheck) const
375{
376 if (applyHasCheck == KEEP_HAS_CHECK) {
377 ENGAUGE_ASSERT (m_hasOrdinal);
378 }
379
380 return m_ordinal;
381}
382
383QPointF Point::posGraph (ApplyHasCheck applyHasCheck) const
384{
385 if (applyHasCheck == KEEP_HAS_CHECK) {
386 ENGAUGE_ASSERT (m_hasPosGraph);
387 }
388
389 return m_posGraph;
390}
391
392QPointF Point::posScreen () const
393{
394 return m_posScreen;
395}
396
397void Point::printStream(QString indentation,
398 QTextStream &str) const
399{
400 const QString UNDEFINED ("undefined");
401
402 str << indentation << "Point\n";
403
404 indentation += INDENTATION_DELTA;
405
406 str << indentation << "identifier=" << m_identifier << "\n";
407 str << indentation << "posScreen=" << QPointFToString (m_posScreen) << "\n";
408 if (m_hasPosGraph) {
409 str << indentation << "posGraph=" << QPointFToString (m_posGraph) << "\n";
410 } else {
411 str << indentation << "posGraph=" << UNDEFINED << "\n";
412 }
413 if (m_hasOrdinal) {
414 str << indentation << "ordinal=" << m_ordinal << "\n";
415 } else {
416 str << indentation << "ordinal=" << UNDEFINED << "\n";
417 }
418}
419
420void Point::saveXml(QXmlStreamWriter &writer) const
421{
422 LOG4CPP_INFO_S ((*mainCat)) << "Point::saveXml";
423
424 writer.writeStartElement(DOCUMENT_SERIALIZE_POINT);
425 writer.writeAttribute(DOCUMENT_SERIALIZE_POINT_IDENTIFIER, m_identifier);
426 if (m_hasOrdinal) {
427 writer.writeAttribute(DOCUMENT_SERIALIZE_POINT_ORDINAL, QString::number (m_ordinal));
428 }
429 writer.writeAttribute(DOCUMENT_SERIALIZE_POINT_IS_AXIS_POINT,
430 m_isAxisPoint ? DOCUMENT_SERIALIZE_BOOL_TRUE : DOCUMENT_SERIALIZE_BOOL_FALSE);
431 writer.writeAttribute(DOCUMENT_SERIALIZE_POINT_IS_X_ONLY,
432 m_isXOnly ? DOCUMENT_SERIALIZE_BOOL_TRUE : DOCUMENT_SERIALIZE_BOOL_FALSE);
433
434 // Variable m_identifierIndex is static, but for simplicity this is handled like other values. Those values are all
435 // the same, but simplicity wins over a few extra bytes of storage
436 writer.writeAttribute(DOCUMENT_SERIALIZE_POINT_IDENTIFIER_INDEX, QString::number (m_identifierIndex));
437
438 writer.writeStartElement(DOCUMENT_SERIALIZE_POINT_POSITION_SCREEN);
439 writer.writeAttribute(DOCUMENT_SERIALIZE_POINT_X, QString::number (m_posScreen.x()));
440 writer.writeAttribute(DOCUMENT_SERIALIZE_POINT_Y, QString::number (m_posScreen.y()));
441 writer.writeEndElement();
442
443 if (m_hasPosGraph) {
444 writer.writeStartElement(DOCUMENT_SERIALIZE_POINT_POSITION_GRAPH);
445 writer.writeAttribute(DOCUMENT_SERIALIZE_POINT_X, QString::number (m_posGraph.x()));
446 writer.writeAttribute(DOCUMENT_SERIALIZE_POINT_Y, QString::number (m_posGraph.y()));
447 writer.writeEndElement();
448 }
449
450 writer.writeEndElement();
451}
452
453void Point::setCurveName(const QString &curveNameNew)
454{
455 // Replace the old curve name at the start of the string
456 QString curveNameOld = Point::curveNameFromPointIdentifier (m_identifier);
457 m_identifier = curveNameNew + m_identifier.mid (curveNameOld.length());
458}
459
460void Point::setIdentifierIndex (unsigned int identifierIndex)
461{
462 LOG4CPP_INFO_S ((*mainCat)) << "Point::setIdentifierIndex"
463 << " identifierIndex=" << identifierIndex;
464
465 m_identifierIndex = identifierIndex;
466}
467
468void Point::setOrdinal(double ordinal)
469{
470 LOG4CPP_DEBUG_S ((*mainCat)) << "Point::setOrdinal"
471 << " identifier=" << m_identifier.toLatin1().data()
472 << " ordinal=" << ordinal;
473
474 m_hasOrdinal = true;
475 m_ordinal = ordinal;
476}
477
478void Point::setPosGraph (const QPointF &posGraph)
479{
480 LOG4CPP_DEBUG_S ((*mainCat)) << "Point::setPosGraph"
481 << " identifier=" << m_identifier.toLatin1().data()
482 << " posGraph=" << QPointFToString(posGraph).toLatin1().data();
483
484 // Curve point graph coordinates should always be computed on the fly versus stored in this class, to reduce the
485 // chances for stale information
486 ENGAUGE_ASSERT (m_isAxisPoint);
487
488 m_hasPosGraph = true;
489 m_posGraph = posGraph;
490}
491
492void Point::setPosScreen (const QPointF &posScreen)
493{
494 LOG4CPP_DEBUG_S ((*mainCat)) << "Point::setPosScreen"
495 << " identifier=" << m_identifier.toLatin1().data()
496 << " posScreen=" << QPointFToString(posScreen).toLatin1().data();
497
498 m_posScreen = posScreen;
499}
500
502{
503 return QString ("%1%2%3")
504 .arg (AXIS_CURVE_NAME)
505 .arg (POINT_IDENTIFIER_DELIMITER_SAFE)
506 .arg (0);
507}
508
509QString Point::uniqueIdentifierGenerator (const QString &curveName)
510{
511 LOG4CPP_INFO_S ((*mainCat)) << "Point::uniqueIdentifierGenerator"
512 << " curveName=" << curveName.toLatin1().data()
513 << " identifierIndex=" << m_identifierIndex;
514
515 return QString ("%1%2point%3%4")
516 .arg (curveName)
517 .arg (POINT_IDENTIFIER_DELIMITER_SAFE)
518 .arg (POINT_IDENTIFIER_DELIMITER_SAFE)
519 .arg (m_identifierIndex++);
520}
Class that represents one digitized point. The screen-to-graph coordinate transformation is always ex...
Definition Point.h:24
void setOrdinal(double ordinal)
Set the ordinal used for ordering Points.
Definition Point.cpp:468
void setCurveName(const QString &curveName)
Update the point identifer to match the specified curve name.
Definition Point.cpp:453
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:397
void setPosScreen(const QPointF &posScreen)
Set method for position in screen coordinates.
Definition Point.cpp:492
static QString curveNameFromPointIdentifier(const QString &pointIdentifier)
Parse the curve name from the specified point identifier. This does the opposite of uniqueIdentifierG...
Definition Point.cpp:227
bool hasPosGraph() const
True if graph position is defined.
Definition Point.cpp:251
QPointF posGraph(ApplyHasCheck applyHasCheck=KEEP_HAS_CHECK) const
Accessor for graph position. Skip check if copying one instance to another.
Definition Point.cpp:383
static unsigned int identifierIndex()
Return the current index for storage in case we need to reset it later while performing a Redo.
Definition Point.cpp:261
QPointF posScreen() const
Accessor for screen position.
Definition Point.cpp:392
QString identifier() const
Unique identifier for a specific Point.
Definition Point.cpp:256
static void setIdentifierIndex(unsigned int identifierIndex)
Reset the current index while performing a Redo.
Definition Point.cpp:460
double ordinal(ApplyHasCheck applyHasCheck=KEEP_HAS_CHECK) const
Get method for ordinal. Skip check if copying one instance to another.
Definition Point.cpp:374
void saveXml(QXmlStreamWriter &writer) const
Serialize to stream.
Definition Point.cpp:420
void setPosGraph(const QPointF &posGraph)
Set method for position in graph coordinates.
Definition Point.cpp:478
bool hasOrdinal() const
True if ordinal is defined.
Definition Point.cpp:246
Point & operator=(const Point &point)
Assignment constructor.
Definition Point.cpp:204
static QString temporaryPointIdentifier()
Point identifier for temporary point that is used by DigitzeStateAxis.
Definition Point.cpp:501
bool isAxisPoint() const
True if point is an axis point. This is used only for sanity checks.
Definition Point.cpp:269
Point()
Default constructor so this class can be used inside a container.
Definition Point.cpp:29
bool isXOnly() const
In DOCUMENT_AXES_POINTS_REQUIRED_4 modes, this is true/false if y/x coordinate is undefined.
Definition Point.cpp:274