Engauge Digitizer  2
 All Classes Functions Variables Typedefs Enumerations Friends Pages
SplineDrawer.cpp
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 
14  m_transformation (transformation)
15 {
16 }
17 
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 
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 
110 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 }
void transformScreenToRawGraph(const QPointF &coordScreen, QPointF &coordGraph) const
Transform from cartesian pixel screen coordinates to cartesian/polar graph coordinates.
SplinePair interpolateCoeff(double t) const
Return interpolated y for specified x.
Definition: Spline.cpp:233
Cubic interpolation given independent and dependent value vectors.
Definition: Spline.h:29
bool segmentIsMultiValued(const Spline &spline, int numSegments, int segment) const
Return true if specified segment is multi-valued, else false.
double y() const
Get method for y.
Definition: SplinePair.cpp:88
SplineDrawerOperation segmentOperation(int segment) const
Indicate if, and how, segment is to be drawn.
SplineDrawer(const Transformation &transformation)
Single constructor.
Affine transformation between screen and graph coordinates, based on digitized axis points...
Details for a specific Line.
Definition: LineStyle.h:19
void bindToSpline(const LineStyle &lineStyle, int numSegments, const Spline &spline)
Analyze each segment in the Spline.
bool transformIsDefined() const
Transform is defined when at least three axis points have been digitized.
double x() const
Get method for x.
Definition: SplinePair.cpp:83
CurveConnectAs curveConnectAs() const
Get method for connect type.
Definition: LineStyle.cpp:63
Single X/Y pair for cubic spline interpolation initialization and calculations.
Definition: SplinePair.h:13