1 /******************************************************************************************************
2 * (C) 2018 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 "EngaugeAssert.h"
8 #include "LineStyle.h"
9 #include <qmath.h>
10 #include "Spline.h"
11 #include "SplineDrawer.h"
12
SplineDrawer(const Transformation & transformation)13 SplineDrawer::SplineDrawer(const Transformation &transformation) :
14 m_transformation (transformation)
15 {
16 }
17
bindToSpline(const LineStyle & lineStyle,int numSegments,const Spline & spline)18 void SplineDrawer::bindToSpline (const LineStyle &lineStyle,
19 int numSegments,
20 const Spline &spline)
21 {
22 m_segmentOperations.resize (numSegments);
23
24 // Loop through segments to get move/draw choice. We do not need to worry about
25 // applying a move (versus a draw) for the first segment since that first point
26 // is handled by external code
27 for (int segment = 0; segment < numSegments; segment++) {
28
29 bool itsAKeeper = true;
30 if (m_transformation.transformIsDefined()) {
31
32 // We have the graph<->screen transformation so let's use it. Could there be an ambiguity issue?
33 if ((lineStyle.curveConnectAs() == CONNECT_AS_FUNCTION_SMOOTH) &&
34 segmentIsMultiValued (spline,
35 numSegments,
36 segment)) {
37 itsAKeeper = false;
38 }
39
40 // Invisible or visible?
41 m_segmentOperations [segment] = (itsAKeeper ?
42 SPLINE_DRAWER_ENUM_VISIBLE_DRAW :
43 SPLINE_DRAWER_ENUM_INVISIBLE_MOVE);
44 }
45 }
46 }
47
segmentIsMultiValued(const Spline & spline,int numSegments,int segment) const48 bool SplineDrawer::segmentIsMultiValued (const Spline &spline,
49 int numSegments,
50 int segment) const
51 {
52 ENGAUGE_ASSERT (m_transformation.transformIsDefined());
53
54 if (segment < numSegments - 1) {
55
56 // Not at very end
57 double tI = double (segment);
58 double tIp1 = double (segment + 1);
59
60 // Compute number of pixels between endpoints
61 SplinePair posScreenStart = spline.interpolateCoeff (tI);
62 SplinePair posScreenEnd = spline.interpolateCoeff (tIp1);
63
64 int deltaX = qFloor (posScreenEnd.x() - posScreenStart.x());
65 int deltaY = qFloor (posScreenEnd.y() - posScreenStart.y());
66 double pixelDistance = qSqrt (deltaX * deltaX + deltaY * deltaY);
67 double numSteps = pixelDistance;
68
69 // Search through a sufficiently large number of points to verify single-valuedness
70 double tIDelta = 1.0 / numSteps;
71 for (int itI = 1; itI < numSteps - 1; itI++) {
72
73 double tIm1 = segment + (itI - 1) * tIDelta;
74 double tI = segment + (itI ) * tIDelta;
75 double tIp1 = segment + (itI + 1) * tIDelta;
76
77 SplinePair spBefore = spline.interpolateCoeff (tIm1);
78 SplinePair spCurrent = spline.interpolateCoeff (tI);
79 SplinePair spAfter = spline.interpolateCoeff (tIp1);
80
81 QPointF posScreenBefore (spBefore.x(), spBefore.y());
82 QPointF posScreenCurrent (spCurrent.x(), spCurrent.y());
83 QPointF posScreenAfter (spAfter.x(), spAfter.y());
84
85 QPointF posGraphBefore, posGraphCurrent, posGraphAfter;
86 m_transformation.transformScreenToRawGraph (posScreenBefore,
87 posGraphBefore);
88 m_transformation.transformScreenToRawGraph (posScreenCurrent,
89 posGraphCurrent);
90 m_transformation.transformScreenToRawGraph (posScreenAfter,
91 posGraphAfter);
92
93 // In between the start and end points we look for deltaXBefore>0 and deltaXAfter<0,
94 // or deltaXBefore<0 and deltaXAfter>0, either of those two cases indicates multi-valued
95 double deltaXBefore = posGraphCurrent.x() - posGraphBefore.x();
96 double deltaXAfter = posGraphAfter.x() - posGraphCurrent.x();
97
98 if ((deltaXBefore > 0 && deltaXAfter < 0) ||
99 (deltaXBefore < 0 && deltaXAfter > 0)) {
100
101 // Multi-valued
102 return true;
103 }
104 }
105 }
106
107 return false;
108 }
109
segmentOperation(int segment) const110 SplineDrawerOperation SplineDrawer::segmentOperation (int segment) const
111 {
112 if (segment < m_segmentOperations.count()) {
113 return m_segmentOperations.at (segment);
114 } else {
115 return SPLINE_DRAWER_ENUM_INVISIBLE_MOVE;
116 }
117 }
118