Engauge Digitizer 2
Loading...
Searching...
No Matches
GraphicsLinesForCurve.cpp
Go to the documentation of this file.
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 "DataKey.h"
8#include "EngaugeAssert.h"
9#include "EnumsToQt.h"
10#include "GeometryWindow.h"
11#include "GraphicsItemType.h"
13#include "GraphicsPoint.h"
14#include "GraphicsScene.h"
15#include "LineStyle.h"
16#include "Logger.h"
17#include "Point.h"
18#include "PointStyle.h"
19#include <QGraphicsItem>
20#include <QMap>
21#include <QPainterPath>
22#include <QPen>
23#include <QTextStream>
24#include "QtToString.h"
25#include "Spline.h"
26#include "SplineDrawer.h"
27#include "Transformation.h"
28#include "ZValues.h"
29
30using namespace std;
31
33
35 m_curveName (curveName)
36{
40 setData (DATA_KEY_IDENTIFIER,
41 QVariant (m_curveName));
42}
43
45{
46 OrdinalToGraphicsPoint::iterator itr;
47 for (itr = m_graphicsPoints.begin(); itr != m_graphicsPoints.end(); itr++) {
48 GraphicsPoint *point = itr.value();
49 delete point;
50 }
51
52 m_graphicsPoints.clear();
53}
54
56 double ordinal,
58{
59 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsLinesForCurve::addPoint"
60 << " curve=" << m_curveName.toLatin1().data()
61 << " identifier=" << pointIdentifier.toLatin1().data()
62 << " ordinal=" << ordinal
63 << " pos=" << QPointFToString (graphicsPoint.pos()).toLatin1().data()
64 << " newPointCount=" << (m_graphicsPoints.count() + 1);
65
66 m_graphicsPoints [ordinal] = &graphicsPoint;
67}
68
69QPainterPath GraphicsLinesForCurve::drawLinesSmooth (const LineStyle &lineStyle,
73{
74 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsLinesForCurve::drawLinesSmooth"
75 << " curve=" << m_curveName.toLatin1().data();
76
77 QPainterPath path;
78
79 // Prepare spline inputs. Note that the ordinal values may not start at 0
82 OrdinalToGraphicsPoint::const_iterator itr;
83 for (itr = m_graphicsPoints.begin(); itr != m_graphicsPoints.end(); itr++) {
84
85 double ordinal = itr.key();
86 const GraphicsPoint *point = itr.value();
87
88 t.push_back (ordinal);
89 xy.push_back (SplinePair (point->pos ().x(),
90 point->pos ().y()));
91 }
92
93 // Spline class requires at least one point
94 if (xy.size() > 0) {
95
96 // Spline through points
97 Spline spline (t, xy);
98
99 splineDrawer.bindToSpline (lineStyle,
100 m_graphicsPoints.count(),
101 spline);
102
103 // Create QPainterPath through the points. Loop has one segment per stop point,
104 // with first point handled outside first
105 int segment; // Only incremented after a draw, corresponding to finishing a segment
106 OrdinalToGraphicsPoint::const_iterator itr = m_graphicsPoints.begin();
107
108 const GraphicsPoint *point = itr.value();
109 path.moveTo (point->pos ());
110 pathMultiValued.moveTo (point->pos ());
111 ++itr;
112
113 for (segment = 0;
114 itr != m_graphicsPoints.end();
115 segment++, itr++) {
116
117 const GraphicsPoint *point = itr.value();
118
119 SplineDrawerOperation operation = splineDrawer.segmentOperation (segment);
120
121 QPointF p1 (spline.p1 (unsigned (segment)).x(),
122 spline.p1 (unsigned (segment)).y());
123 QPointF p2 (spline.p2 (unsigned (segment)).x(),
124 spline.p2 (unsigned (segment)).y());
125
126 switch (operation) {
128 {
129 // Show this segment
130 path.cubicTo (p1,
131 p2,
132 point->pos ());
133 }
134 break;
135
137
138 // Hide this segment as a regular curve, and show it as the error curve
139 path.moveTo (point->pos ());
140
141 // Show curveMultiValued instead in what would have been the original curve's path
142 OrdinalToGraphicsPoint::const_iterator itrBefore = itr - 1;
143 const GraphicsPoint *pointBefore = itrBefore.value();
144 pathMultiValued.moveTo (pointBefore->pos ());
145 pathMultiValued.cubicTo (p1,
146 p2,
147 point->pos ());
148 lineMultiValued = lineStyle; // Remember to not use the same line style
149 break;
150
151 }
152
153 // Always move to next point for curveMultiValued
154 pathMultiValued.moveTo (point->pos ());
155 }
156 }
157
158 return path;
159}
160
161QPainterPath GraphicsLinesForCurve::drawLinesStraight (QPainterPath & /* pathMultiValued */)
162{
163 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsLinesForCurve::drawLinesStraight"
164 << " curve=" << m_curveName.toLatin1().data();
165
166 QPainterPath path;
167
168 // Create QPainterPath through the points
169 bool isFirst = true;
170 OrdinalToGraphicsPoint::const_iterator itr;
171 for (itr = m_graphicsPoints.begin(); itr != m_graphicsPoints.end(); itr++) {
172
173 const GraphicsPoint *point = itr.value();
174
175 if (isFirst) {
176 isFirst = false;
177 path.moveTo (point->pos ());
178 } else {
179 path.lineTo (point->pos ());
180 }
181 }
182
183 return path;
184}
185
187{
188 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsLinesForCurve::identifierToOrdinal"
189 << " identifier=" << identifier.toLatin1().data();
190
191 OrdinalToGraphicsPoint::const_iterator itr;
192 for (itr = m_graphicsPoints.begin(); itr != m_graphicsPoints.end(); itr++) {
193
194 const GraphicsPoint *point = itr.value();
195
196 if (point->data (DATA_KEY_IDENTIFIER) == identifier) {
197 return itr.key();
198 }
199 }
200
201 ENGAUGE_ASSERT (false);
202
203 return 0;
204}
205
210{
211 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsLinesForCurve::lineMembershipPurge"
212 << " curve=" << m_curveName.toLatin1().data();
213
214 OrdinalToGraphicsPoint::iterator itr, itrNext;
215 for (itr = m_graphicsPoints.begin(); itr != m_graphicsPoints.end(); itr = itrNext) {
216
217 itrNext = itr;
218 ++itrNext;
219
220 GraphicsPoint *point = *itr;
221
222 if (!point->wanted ()) {
223
224 double ordinal = itr.key ();
225
226 delete point;
227 m_graphicsPoints.remove (ordinal);
228 }
229 }
230
231 // Apply line style
232 QPen pen;
233 if (lineStyle.paletteColor() == COLOR_PALETTE_TRANSPARENT) {
234
235 pen = QPen (Qt::NoPen);
236
237 } else {
238
239 pen = QPen (QBrush (ColorPaletteToQColor (lineStyle.paletteColor())),
240 lineStyle.width());
241
242 }
243
244 setPen (pen);
245
250}
251
253{
254 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsLinesForCurve::lineMembershipReset"
255 << " curve=" << m_curveName.toLatin1().data();
256
257 OrdinalToGraphicsPoint::iterator itr;
258 for (itr = m_graphicsPoints.begin(); itr != m_graphicsPoints.end(); itr++) {
259
260 GraphicsPoint *point = itr.value();
261
262 point->reset ();
263 }
264}
265
266bool GraphicsLinesForCurve::needOrdinalRenumbering () const
267{
268 // Ordinals should be 0, 1, ...
269 bool needRenumbering = false;
270 for (int ordinalKeyWanted = 0; ordinalKeyWanted < m_graphicsPoints.count(); ordinalKeyWanted++) {
271
272 double ordinalKeyGot = m_graphicsPoints.keys().at (ordinalKeyWanted);
273
274 // Sanity checks
276
278 needRenumbering = true;
279 break;
280 }
281 }
282
283 return needRenumbering;
284}
285
287 QTextStream &str) const
288{
289 DataKey type = static_cast<DataKey> (data (DATA_KEY_GRAPHICS_ITEM_TYPE).toInt());
290
291 str << indentation << "GraphicsLinesForCurve=" << m_curveName
292 << " dataIdentifier=" << data (DATA_KEY_IDENTIFIER).toString().toLatin1().data()
293 << " dataType=" << dataKeyToString (type).toLatin1().data() << "\n";
294
296
297 OrdinalToGraphicsPoint::const_iterator itr;
298 for (itr = m_graphicsPoints.begin(); itr != m_graphicsPoints.end(); itr++) {
299
300 double ordinalKey = itr.key();
301 const GraphicsPoint *point = itr.value();
302
303 point->printStream (indentation,
304 str,
305 ordinalKey);
306 }
307}
308
310{
311 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsLinesForCurve::removePoint"
312 << " point=" << ordinal
313 << " pointCount=" << m_graphicsPoints.count();
314
315 ENGAUGE_ASSERT (m_graphicsPoints.contains (ordinal));
316 GraphicsPoint *graphicsPoint = m_graphicsPoints [ordinal];
317
318 m_graphicsPoints.remove (ordinal);
319
320 delete graphicsPoint;
321}
322
324{
325 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsLinesForCurve::removeTemporaryPointIfExists";
326
327 // Compiler warning about this loop only iterating once is not an issue since there
328 // is never more than one temporary point
329 OrdinalToGraphicsPoint::iterator itr;
330 for (itr = m_graphicsPoints.begin(); itr != m_graphicsPoints.end(); itr++) {
331
333
334 m_graphicsPoints.remove (itr.key());
335
336 delete graphicsPoint;
337
338 break;
339 }
340}
341
342void GraphicsLinesForCurve::renumberOrdinals ()
343{
344 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsLinesForCurve::renumberOrdinals";
345
347
348 // Ordinals should be 0, 1, and so on. Assigning a list to QMap::keys has no effect, so the
349 // approach is to copy to a temporary list and then copy back
351 for (ordinalKeyWanted = 0; ordinalKeyWanted < m_graphicsPoints.count(); ordinalKeyWanted++) {
352
353 GraphicsPoint *graphicsPoint = m_graphicsPoints.values().at (ordinalKeyWanted);
354 points << graphicsPoint;
355 }
356
357 m_graphicsPoints.clear ();
358
359 for (ordinalKeyWanted = 0; ordinalKeyWanted < points.count(); ordinalKeyWanted++) {
360
362 m_graphicsPoints [ordinalKeyWanted] = graphicsPoint;
363 }
364}
365
367 const PointStyle &pointStyle,
368 const Point &point,
370{
371 LOG4CPP_DEBUG_S ((*mainCat)) << "GraphicsLinesForCurve::updateAfterCommand"
372 << " curve=" << m_curveName.toLatin1().data()
373 << " pointCount=" << m_graphicsPoints.count();
374
375 GraphicsPoint *graphicsPoint = nullptr;
376 if (m_graphicsPoints.contains (point.ordinal())) {
377
378 graphicsPoint = m_graphicsPoints [point.ordinal()];
379
380 // Due to ordinal renumbering, the coordinates may belong to some other point so we override
381 // them for consistent ordinal-position mapping. Updating the identifier also was added for
382 // better logging (i.e. consistency between Document and GraphicsScene dumps), but happened
383 // to fix a bug with the wrong set of points getting deleted from Cut and Delete
384 graphicsPoint->setPos (point.posScreen());
385 graphicsPoint->setData (DATA_KEY_IDENTIFIER, point.identifier());
386
387 } else {
388
389 // Point does not exist in scene so create it
390 graphicsPoint = scene.createPoint (point.identifier (),
391 pointStyle,
392 point.posScreen(),
394 m_graphicsPoints [point.ordinal ()] = graphicsPoint;
395
396 }
397
398 // Mark point as wanted
400 graphicsPoint->setWanted ();
401}
402
404{
405 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsLinesForCurve::updateCurveStyle";
406
407 OrdinalToGraphicsPoint::const_iterator itr;
408 for (itr = m_graphicsPoints.begin(); itr != m_graphicsPoints.end(); itr++) {
409
410 GraphicsPoint *point = itr.value();
411 point->updateCurveStyle (curveStyle);
412 }
413}
414
416{
417 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsLinesForCurve::updateCurveStyle"
418 << " curve=" << m_curveName.toLatin1().data()
419 << " highlightOpacity=" << highlightOpacity;
420
421 OrdinalToGraphicsPoint::const_iterator itr;
422 for (itr = m_graphicsPoints.begin(); itr != m_graphicsPoints.end(); itr++) {
423
424 GraphicsPoint *point = itr.value();
425 point->setHighlightOpacity (highlightOpacity);
426 }
427}
428
433{
434 // LOG4CPP_INFO_S is below
435
436 bool needRenumbering = needOrdinalRenumbering ();
437 if (needRenumbering) {
438
439 renumberOrdinals();
440
441 }
442
443 LOG4CPP_INFO_S ((*mainCat)) << "GraphicsLinesForCurve::updateGraphicsLinesToMatchGraphicsPoints"
444 << " numberPoints=" << m_graphicsPoints.count()
445 << " ordinalRenumbering=" << (needRenumbering ? "true" : "false");
446
447 if (lineStyle.curveConnectAs() != CONNECT_SKIP_FOR_AXIS_CURVE) {
448
449 // Draw as either straight or smoothed. The function/relation differences were handled already with ordinals. The
450 // Spline algorithm will crash with fewer than three points so it is only called when there are enough points
451 QPainterPath path;
452 if (lineStyle.curveConnectAs() == CONNECT_AS_FUNCTION_STRAIGHT ||
454 m_graphicsPoints.count () < 3) {
455
456 path = drawLinesStraight (pathMultiValued);
457 } else {
458 path = drawLinesSmooth (lineStyle,
462 }
463
464 setPath (path);
465 }
466}
467
469 const Transformation &transformation)
470{
471 CurveConnectAs curveConnectAs = lineStyle.curveConnectAs();
472
473 LOG4CPP_DEBUG_S ((*mainCat)) << "GraphicsLinesForCurve::updatePointOrdinalsAfterDrag"
474 << " curve=" << m_curveName.toLatin1().data()
475 << " curveConnectAs=" << curveConnectAsToString(curveConnectAs).toLatin1().data();
476
477 if (curveConnectAs == CONNECT_AS_FUNCTION_SMOOTH ||
478 curveConnectAs == CONNECT_AS_FUNCTION_STRAIGHT) {
479
480 // Make sure ordinals are properly ordered
481
482 // Get a map of x/theta values as keys with point identifiers as the values
484 OrdinalToGraphicsPoint::iterator itrP;
485 for (itrP = m_graphicsPoints.begin(); itrP != m_graphicsPoints.end(); itrP++) {
486
487 double ordinal = itrP.key();
488 const GraphicsPoint *point = itrP.value();
489
490 // Convert screen coordinate to graph coordinates, which gives us x/theta
492 transformation.transformScreenToRawGraph(point->pos (),
493 pointGraph);
494
495 xOrThetaToOrdinal [pointGraph.x()] = ordinal;
496 }
497
498 // Loop through the sorted x/theta values. Since QMap is used, the x/theta keys are sorted
500 int ordinalNew = 0;
501 XOrThetaToOrdinal::const_iterator itrX;
502 for (itrX = xOrThetaToOrdinal.begin(); itrX != xOrThetaToOrdinal.end(); itrX++) {
503
504 double ordinalOld = *itrX;
505 GraphicsPoint *point = m_graphicsPoints [ordinalOld];
506
507 temporaryList [ordinalNew++] = point;
508 }
509
510 // Copy from temporary back to original map
511 m_graphicsPoints.clear();
512 for (itrP = temporaryList.begin(); itrP != temporaryList.end(); itrP++) {
513
514 double ordinal = itrP.key();
515 GraphicsPoint *point = itrP.value();
516
517 m_graphicsPoints [ordinal] = point;
518 }
519 }
520}
@ COLOR_PALETTE_TRANSPARENT
QString curveConnectAsToString(CurveConnectAs curveConnectAs)
CurveConnectAs
@ CONNECT_AS_FUNCTION_STRAIGHT
@ CONNECT_AS_RELATION_STRAIGHT
@ CONNECT_SKIP_FOR_AXIS_CURVE
@ CONNECT_AS_FUNCTION_SMOOTH
QString dataKeyToString(DataKey dataKey)
Definition DataKey.cpp:9
DataKey
Index values for storing item details in QGraphicsItem using setData/data.
Definition DataKey.h:13
@ DATA_KEY_GRAPHICS_ITEM_TYPE
‍Unique identifier for QGraphicsItem object
Definition DataKey.h:15
@ DATA_KEY_IDENTIFIER
Definition DataKey.h:14
const int INNER_RADIUS_MIN
#define ENGAUGE_ASSERT(cond)
Drop in replacement for Q_ASSERT if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS) define ENGAUGE...
#define ENGAUGE_CHECK_PTR(ptr)
#endif
QColor ColorPaletteToQColor(ColorPalette color)
Definition EnumsToQt.cpp:15
@ GRAPHICS_ITEM_TYPE_LINE
QMap< double, double > XOrThetaToOrdinal
log4cpp::Category * mainCat
Definition Logger.cpp:14
const QString INDENTATION_DELTA
QMap< double, GraphicsPoint * > OrdinalToGraphicsPoint
QString QPointFToString(const QPointF &pos)
SplineDrawerOperation
@ SPLINE_DRAWER_ENUM_INVISIBLE_MOVE
@ SPLINE_DRAWER_ENUM_VISIBLE_DRAW
const int Z_VALUE_CURVE
Definition ZValues.cpp:10
Container for LineStyle and PointStyle for one Curve.
Definition CurveStyle.h:19
Window that displays the geometry information, as a table, for the current curve.
double identifierToOrdinal(const QString &identifier) const
Get ordinal for specified identifier.
void updateAfterCommand(GraphicsScene &scene, const PointStyle &pointStyle, const Point &point, GeometryWindow *geometryWindow)
Update the GraphicsScene with the specified Point from the Document. If it does not exist yet in the ...
void updateCurveStyle(const CurveStyle &curveStyle)
Update the curve style for this curve.
void lineMembershipPurge(const LineStyle &lineStyle, SplineDrawer &splineDrawer, QPainterPath &pathMultiValued, LineStyle &lineMultiValued)
Mark the end of addPoint calls. Remove stale lines, insert missing lines, and draw the graphics lines...
void updateHighlightOpacity(double highlightOpacity)
Update the highlight opacity value. This may or may not affect the current display immediately depend...
void removePoint(double ordinal)
Remove the specified point. The act of deleting it will automatically remove it from the GraphicsScen...
void addPoint(const QString &pointIdentifier, double ordinal, GraphicsPoint &point)
Add new line.
void lineMembershipReset()
Mark points as unwanted. Afterwards, lineMembershipPurge gets called.
void removeTemporaryPointIfExists()
Remove temporary point if it exists.
void updatePointOrdinalsAfterDrag(const LineStyle &lineStyle, const Transformation &transformation)
See GraphicsScene::updateOrdinalsAfterDrag. Pretty much the same steps as Curve::updatePointOrdinals.
GraphicsLinesForCurve(const QString &curveName)
Single constructor.
void printStream(QString indentation, QTextStream &str) const
Debugging method that supports print method of this class and printStream method of some other class(...
void updateGraphicsLinesToMatchGraphicsPoints(const LineStyle &lineStyle, SplineDrawer &splineDrawer, QPainterPath &pathMultiValued, LineStyle &lineMultiValued)
Calls to moveLinesWithDraggedPoint have finished so update the lines correspondingly.
Graphics item for drawing a circular or polygonal Point.
QPointF pos() const
Proxy method for QGraphicsItem::pos.
void updateCurveStyle(const CurveStyle &curveStyle)
Update point and line styles that comprise the curve style.
void reset()
Mark point as unwanted, and unbind any bound lines.
bool wanted() const
Identify point as wanted//unwanted.
void printStream(QString indentation, QTextStream &str, double ordinalKey) const
Debugging method that supports print method of this class and printStream method of some other class(...
void setHighlightOpacity(double highlightOpacity)
Set method for highlight opacity.
QVariant data(int key) const
Proxy method for QGraphicsItem::data.
Add point and line handling to generic QGraphicsScene.
GraphicsPoint * createPoint(const QString &identifier, const PointStyle &pointStyle, const QPointF &posScreen, GeometryWindow *geometryWindow)
Create one QGraphicsItem-based object that represents one Point. It is NOT added to m_graphicsLinesFo...
Details for a specific Line.
Definition LineStyle.h:20
CurveConnectAs curveConnectAs() const
Get method for connect type.
Definition LineStyle.cpp:63
unsigned int width() const
Width of line.
ColorPalette paletteColor() const
Line color.
Details for a specific Point.
Definition PointStyle.h:21
Class that represents one digitized point. The screen-to-graph coordinate transformation is always ex...
Definition Point.h:26
QPointF posScreen() const
Accessor for screen position.
Definition Point.cpp:404
QString identifier() const
Unique identifier for a specific Point.
Definition Point.cpp:268
double ordinal(ApplyHasCheck applyHasCheck=KEEP_HAS_CHECK) const
Get method for ordinal. Skip check if copying one instance to another.
Definition Point.cpp:386
static double UNDEFINED_ORDINAL()
Get method for undefined ordinal constant.
Definition Point.h:134
This class takes the output from Spline and uses that to draw the curve in the graphics window,...
Single X/Y pair for cubic spline interpolation initialization and calculations.
Definition SplinePair.h:14
Cubic interpolation given independent and dependent value vectors.
Definition Spline.h:30
Affine transformation between screen and graph coordinates, based on digitized axis points.
void transformScreenToRawGraph(const QPointF &coordScreen, QPointF &coordGraph) const
Transform from cartesian pixel screen coordinates to cartesian/polar graph coordinates.
#define LOG4CPP_INFO_S(logger)
Definition convenience.h:18
#define LOG4CPP_DEBUG_S(logger)
Definition convenience.h:20