1/****************************************************************************
2**
3** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "functionrangefinder_p.h"
38
39QT_BEGIN_NAMESPACE
40
41namespace Qt3DAnimation {
42namespace Animation {
43
44/*!
45 \internal
46 \class FunctionRangeFinder finds the lower bound index of a range that encloses a function value
47
48 Given a vector of function values (typically abscissa values of some other function), this
49 class can find the lower bound index of a range that encloses the requested value. This is
50 very useful for finding the two points that sandwich a value to which you later wish to
51 interpolate for example.
52 */
53
54/*
55 \internal
56
57 int findLowerBound (float x)
58
59 Finds the lower bound index of a range that encloses the requested value \a x.
60
61 We use a technique which tries to be better than a simple bisection. Often when
62 performing interpolations, subsequent points are correlated with earlier calls.
63 This is especially true with time based lookups. If two calls are determined to
64 be correlated, then the next subsequent call will use the hunt function to search
65 close to the last returned value first. The hunt algorithms searches outward in
66 increasing step sizes until a sandwiching range is found. Traditional bisection
67 is then used to refine this result.
68
69 If the previous results are uncorrelated, a simple bisection is used.
70 */
71
72FunctionRangeFinder::FunctionRangeFinder(const QVector<float> &x)
73 : m_x(x)
74 , m_previousLowerBound(0)
75 , m_correlated(0)
76 , m_rangeSize(2)
77 , m_correlationThreshold(1)
78 , m_ascending(true)
79{
80 updateAutomaticCorrelationThreshold();
81 if (!m_x.isEmpty())
82 m_ascending = (m_x.last() >= m_x.first());
83}
84
85/*!
86 \internal
87 Locates the lower bound of a range that encloses \a x by a bisection method.
88*/
89int FunctionRangeFinder::locate(float x) const
90{
91 if (m_x.size() < 2 || m_rangeSize < 2 || m_rangeSize > m_x.size())
92 return -1;
93
94 int jLower = 0;
95 int jUpper = m_x.size() - 1;
96 while (jUpper - jLower > 1) {
97 int jMid = (jUpper + jLower) >> 1;
98 if ((x >= m_x[jMid]) == m_ascending)
99 jLower = jMid;
100 else
101 jUpper = jMid;
102 }
103
104 m_correlated = std::abs(x: jLower - m_previousLowerBound) <= m_correlationThreshold;
105 m_previousLowerBound = jLower;
106
107 return std::max(a: 0, b: std::min(a: m_x.size() - m_rangeSize, b: jLower - ((m_rangeSize - 2) >> 1)));
108}
109
110/*!
111 \internal
112 Hunts outward from the previous result in increasing step sizes then refines via bisection.
113 */
114int FunctionRangeFinder::hunt(float x) const
115{
116 if (m_x.size() < 2 || m_rangeSize < 2 || m_rangeSize > m_x.size())
117 return -1;
118
119 int jLower = m_previousLowerBound;
120 int jMid;
121 int jUpper;
122 if (jLower < 0 || jLower > (m_x.size() - 1)) {
123 jLower = 0;
124 jUpper = m_x.size() - 1;
125 } else {
126 int increment = 1;
127 if ((x >= m_x[jLower]) == m_ascending) {
128 for (;;) {
129 jUpper = jLower + increment;
130 if (jUpper >= m_x.size() - 1) {
131 jUpper = m_x.size() - 1;
132 break;
133 } else if ((x < m_x[jUpper]) == m_ascending) {
134 break;
135 } else {
136 jLower = jUpper;
137 increment += increment;
138 }
139 }
140 } else {
141 jUpper = jLower;
142 for (;;) {
143 jLower = jLower - increment;
144 if (jLower <= 0) {
145 jLower = 0;
146 break;
147 } else if ((x >= m_x[jLower]) == m_ascending) {
148 break;
149 } else {
150 jUpper = jLower;
151 increment += increment;
152 }
153 }
154 }
155 }
156
157 while (jUpper - jLower > 1) {
158 jMid = (jUpper + jLower) >> 1;
159 if ((x >= m_x[jMid]) == m_ascending)
160 jLower = jMid;
161 else
162 jUpper = jMid;
163 }
164
165 m_correlated = std::abs(x: jLower - m_previousLowerBound) <= m_correlationThreshold;
166 m_previousLowerBound = jLower;
167
168 return std::max(a: 0, b: std::min(a: m_x.size() - m_rangeSize, b: jLower - ((m_rangeSize - 2) >> 1)));
169}
170
171} // namespace Animation
172} // namespace Qt3DAnimation
173
174QT_END_NAMESPACE
175

source code of qt3d/src/animation/backend/functionrangefinder.cpp