1 | /**************************************************************************** |
---|---|

2 | ** |

3 | ** Copyright (C) 2016 The Qt Company Ltd. |

4 | ** Contact: https://www.qt.io/licensing/ |

5 | ** |

6 | ** This file is part of the QtCore module of the Qt Toolkit. |

7 | ** |

8 | ** $QT_BEGIN_LICENSE:LGPL$ |

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 https://www.qt.io/terms-conditions. For further |

15 | ** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General |

28 | ** Public license version 3 or any later version approved by the KDE Free |

29 | ** Qt Foundation. The licenses are as published by the Free Software |

30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |

31 | ** included in the packaging of this file. Please review the following |

32 | ** information to ensure the GNU General Public License requirements will |

33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |

34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |

35 | ** |

36 | ** $QT_END_LICENSE$ |

37 | ** |

38 | ****************************************************************************/ |

39 | |

40 | /* |

41 | |

42 | | *property* | *Used for type* | |

43 | | period | QEasingCurve::{In,Out,InOut,OutIn}Elastic | |

44 | | amplitude | QEasingCurve::{In,Out,InOut,OutIn}Bounce, QEasingCurve::{In,Out,InOut,OutIn}Elastic | |

45 | | overshoot | QEasingCurve::{In,Out,InOut,OutIn}Back | |

46 | |

47 | */ |

48 | |

49 | |

50 | |

51 | |

52 | /*! |

53 | \class QEasingCurve |

54 | \inmodule QtCore |

55 | \since 4.6 |

56 | \ingroup animation |

57 | \brief The QEasingCurve class provides easing curves for controlling animation. |

58 | |

59 | Easing curves describe a function that controls how the speed of the interpolation |

60 | between 0 and 1 should be. Easing curves allow transitions from |

61 | one value to another to appear more natural than a simple constant speed would allow. |

62 | The QEasingCurve class is usually used in conjunction with the QVariantAnimation and |

63 | QPropertyAnimation classes but can be used on its own. It is usually used to accelerate |

64 | the interpolation from zero velocity (ease in) or decelerate to zero velocity (ease out). |

65 | Ease in and ease out can also be combined in the same easing curve. |

66 | |

67 | To calculate the speed of the interpolation, the easing curve provides the function |

68 | valueForProgress(), where the \a progress argument specifies the progress of the |

69 | interpolation: 0 is the start value of the interpolation, 1 is the end value of the |

70 | interpolation. The returned value is the effective progress of the interpolation. |

71 | If the returned value is the same as the input value for all input values the easing |

72 | curve is a linear curve. This is the default behaviour. |

73 | |

74 | For example, |

75 | |

76 | \snippet code/src_corelib_tools_qeasingcurve.cpp 0 |

77 | |

78 | will print the effective progress of the interpolation between 0 and 1. |

79 | |

80 | When using a QPropertyAnimation, the associated easing curve will be used to control the |

81 | progress of the interpolation between startValue and endValue: |

82 | |

83 | \snippet code/src_corelib_tools_qeasingcurve.cpp 1 |

84 | |

85 | The ability to set an amplitude, overshoot, or period depends on |

86 | the QEasingCurve type. Amplitude access is available to curves |

87 | that behave as springs such as elastic and bounce curves. Changing |

88 | the amplitude changes the height of the curve. Period access is |

89 | only available to elastic curves and setting a higher period slows |

90 | the rate of bounce. Only curves that have "boomerang" behaviors |

91 | such as the InBack, OutBack, InOutBack, and OutInBack have |

92 | overshoot settings. These curves will interpolate beyond the end |

93 | points and return to the end point, acting similar to a boomerang. |

94 | |

95 | The \l{Easing Curves Example} contains samples of QEasingCurve |

96 | types and lets you change the curve settings. |

97 | |

98 | */ |

99 | |

100 | /*! |

101 | \enum QEasingCurve::Type |

102 | |

103 | The type of easing curve. |

104 | |

105 | \value Linear \image qeasingcurve-linear.png |

106 | \caption Easing curve for a linear (t) function: |

107 | velocity is constant. |

108 | \value InQuad \image qeasingcurve-inquad.png |

109 | \caption Easing curve for a quadratic (t^2) function: |

110 | accelerating from zero velocity. |

111 | \value OutQuad \image qeasingcurve-outquad.png |

112 | \caption Easing curve for a quadratic (t^2) function: |

113 | decelerating to zero velocity. |

114 | \value InOutQuad \image qeasingcurve-inoutquad.png |

115 | \caption Easing curve for a quadratic (t^2) function: |

116 | acceleration until halfway, then deceleration. |

117 | \value OutInQuad \image qeasingcurve-outinquad.png |

118 | \caption Easing curve for a quadratic (t^2) function: |

119 | deceleration until halfway, then acceleration. |

120 | \value InCubic \image qeasingcurve-incubic.png |

121 | \caption Easing curve for a cubic (t^3) function: |

122 | accelerating from zero velocity. |

123 | \value OutCubic \image qeasingcurve-outcubic.png |

124 | \caption Easing curve for a cubic (t^3) function: |

125 | decelerating to zero velocity. |

126 | \value InOutCubic \image qeasingcurve-inoutcubic.png |

127 | \caption Easing curve for a cubic (t^3) function: |

128 | acceleration until halfway, then deceleration. |

129 | \value OutInCubic \image qeasingcurve-outincubic.png |

130 | \caption Easing curve for a cubic (t^3) function: |

131 | deceleration until halfway, then acceleration. |

132 | \value InQuart \image qeasingcurve-inquart.png |

133 | \caption Easing curve for a quartic (t^4) function: |

134 | accelerating from zero velocity. |

135 | \value OutQuart \image qeasingcurve-outquart.png |

136 | \caption |

137 | Easing curve for a quartic (t^4) function: |

138 | decelerating to zero velocity. |

139 | \value InOutQuart \image qeasingcurve-inoutquart.png |

140 | \caption |

141 | Easing curve for a quartic (t^4) function: |

142 | acceleration until halfway, then deceleration. |

143 | \value OutInQuart \image qeasingcurve-outinquart.png |

144 | \caption |

145 | Easing curve for a quartic (t^4) function: |

146 | deceleration until halfway, then acceleration. |

147 | \value InQuint \image qeasingcurve-inquint.png |

148 | \caption |

149 | Easing curve for a quintic (t^5) easing |

150 | in: accelerating from zero velocity. |

151 | \value OutQuint \image qeasingcurve-outquint.png |

152 | \caption |

153 | Easing curve for a quintic (t^5) function: |

154 | decelerating to zero velocity. |

155 | \value InOutQuint \image qeasingcurve-inoutquint.png |

156 | \caption |

157 | Easing curve for a quintic (t^5) function: |

158 | acceleration until halfway, then deceleration. |

159 | \value OutInQuint \image qeasingcurve-outinquint.png |

160 | \caption |

161 | Easing curve for a quintic (t^5) function: |

162 | deceleration until halfway, then acceleration. |

163 | \value InSine \image qeasingcurve-insine.png |

164 | \caption |

165 | Easing curve for a sinusoidal (sin(t)) function: |

166 | accelerating from zero velocity. |

167 | \value OutSine \image qeasingcurve-outsine.png |

168 | \caption |

169 | Easing curve for a sinusoidal (sin(t)) function: |

170 | decelerating to zero velocity. |

171 | \value InOutSine \image qeasingcurve-inoutsine.png |

172 | \caption |

173 | Easing curve for a sinusoidal (sin(t)) function: |

174 | acceleration until halfway, then deceleration. |

175 | \value OutInSine \image qeasingcurve-outinsine.png |

176 | \caption |

177 | Easing curve for a sinusoidal (sin(t)) function: |

178 | deceleration until halfway, then acceleration. |

179 | \value InExpo \image qeasingcurve-inexpo.png |

180 | \caption |

181 | Easing curve for an exponential (2^t) function: |

182 | accelerating from zero velocity. |

183 | \value OutExpo \image qeasingcurve-outexpo.png |

184 | \caption |

185 | Easing curve for an exponential (2^t) function: |

186 | decelerating to zero velocity. |

187 | \value InOutExpo \image qeasingcurve-inoutexpo.png |

188 | \caption |

189 | Easing curve for an exponential (2^t) function: |

190 | acceleration until halfway, then deceleration. |

191 | \value OutInExpo \image qeasingcurve-outinexpo.png |

192 | \caption |

193 | Easing curve for an exponential (2^t) function: |

194 | deceleration until halfway, then acceleration. |

195 | \value InCirc \image qeasingcurve-incirc.png |

196 | \caption |

197 | Easing curve for a circular (sqrt(1-t^2)) function: |

198 | accelerating from zero velocity. |

199 | \value OutCirc \image qeasingcurve-outcirc.png |

200 | \caption |

201 | Easing curve for a circular (sqrt(1-t^2)) function: |

202 | decelerating to zero velocity. |

203 | \value InOutCirc \image qeasingcurve-inoutcirc.png |

204 | \caption |

205 | Easing curve for a circular (sqrt(1-t^2)) function: |

206 | acceleration until halfway, then deceleration. |

207 | \value OutInCirc \image qeasingcurve-outincirc.png |

208 | \caption |

209 | Easing curve for a circular (sqrt(1-t^2)) function: |

210 | deceleration until halfway, then acceleration. |

211 | \value InElastic \image qeasingcurve-inelastic.png |

212 | \caption |

213 | Easing curve for an elastic |

214 | (exponentially decaying sine wave) function: |

215 | accelerating from zero velocity. The peak amplitude |

216 | can be set with the \e amplitude parameter, and the |

217 | period of decay by the \e period parameter. |

218 | \value OutElastic \image qeasingcurve-outelastic.png |

219 | \caption |

220 | Easing curve for an elastic |

221 | (exponentially decaying sine wave) function: |

222 | decelerating to zero velocity. The peak amplitude |

223 | can be set with the \e amplitude parameter, and the |

224 | period of decay by the \e period parameter. |

225 | \value InOutElastic \image qeasingcurve-inoutelastic.png |

226 | \caption |

227 | Easing curve for an elastic |

228 | (exponentially decaying sine wave) function: |

229 | acceleration until halfway, then deceleration. |

230 | \value OutInElastic \image qeasingcurve-outinelastic.png |

231 | \caption |

232 | Easing curve for an elastic |

233 | (exponentially decaying sine wave) function: |

234 | deceleration until halfway, then acceleration. |

235 | \value InBack \image qeasingcurve-inback.png |

236 | \caption |

237 | Easing curve for a back (overshooting |

238 | cubic function: (s+1)*t^3 - s*t^2) easing in: |

239 | accelerating from zero velocity. |

240 | \value OutBack \image qeasingcurve-outback.png |

241 | \caption |

242 | Easing curve for a back (overshooting |

243 | cubic function: (s+1)*t^3 - s*t^2) easing out: |

244 | decelerating to zero velocity. |

245 | \value InOutBack \image qeasingcurve-inoutback.png |

246 | \caption |

247 | Easing curve for a back (overshooting |

248 | cubic function: (s+1)*t^3 - s*t^2) easing in/out: |

249 | acceleration until halfway, then deceleration. |

250 | \value OutInBack \image qeasingcurve-outinback.png |

251 | \caption |

252 | Easing curve for a back (overshooting |

253 | cubic easing: (s+1)*t^3 - s*t^2) easing out/in: |

254 | deceleration until halfway, then acceleration. |

255 | \value InBounce \image qeasingcurve-inbounce.png |

256 | \caption |

257 | Easing curve for a bounce (exponentially |

258 | decaying parabolic bounce) function: accelerating |

259 | from zero velocity. |

260 | \value OutBounce \image qeasingcurve-outbounce.png |

261 | \caption |

262 | Easing curve for a bounce (exponentially |

263 | decaying parabolic bounce) function: decelerating |

264 | from zero velocity. |

265 | \value InOutBounce \image qeasingcurve-inoutbounce.png |

266 | \caption |

267 | Easing curve for a bounce (exponentially |

268 | decaying parabolic bounce) function easing in/out: |

269 | acceleration until halfway, then deceleration. |

270 | \value OutInBounce \image qeasingcurve-outinbounce.png |

271 | \caption |

272 | Easing curve for a bounce (exponentially |

273 | decaying parabolic bounce) function easing out/in: |

274 | deceleration until halfway, then acceleration. |

275 | \omitvalue InCurve |

276 | \omitvalue OutCurve |

277 | \omitvalue SineCurve |

278 | \omitvalue CosineCurve |

279 | \value BezierSpline Allows defining a custom easing curve using a cubic bezier spline |

280 | \sa addCubicBezierSegment() |

281 | \value TCBSpline Allows defining a custom easing curve using a TCB spline |

282 | \sa addTCBSegment() |

283 | \value Custom This is returned if the user specified a custom curve type with |

284 | setCustomType(). Note that you cannot call setType() with this value, |

285 | but type() can return it. |

286 | \omitvalue NCurveTypes |

287 | */ |

288 | |

289 | /*! |

290 | \typedef QEasingCurve::EasingFunction |

291 | |

292 | This is a typedef for a pointer to a function with the following |

293 | signature: |

294 | |

295 | \snippet code/src_corelib_tools_qeasingcurve.cpp typedef |

296 | */ |

297 | |

298 | #include "qeasingcurve.h" |

299 | #include <cmath> |

300 | |

301 | #ifndef QT_NO_DEBUG_STREAM |

302 | #include <QtCore/qdebug.h> |

303 | #include <QtCore/qstring.h> |

304 | #endif |

305 | |

306 | #ifndef QT_NO_DATASTREAM |

307 | #include <QtCore/qdatastream.h> |

308 | #endif |

309 | |

310 | #include <QtCore/qpoint.h> |

311 | #include <QtCore/qvector.h> |

312 | |

313 | QT_BEGIN_NAMESPACE |

314 | |

315 | static bool isConfigFunction(QEasingCurve::Type type) |

316 | { |

317 | return (type >= QEasingCurve::InElastic |

318 | && type <= QEasingCurve::OutInBounce) || |

319 | type == QEasingCurve::BezierSpline || |

320 | type == QEasingCurve::TCBSpline; |

321 | } |

322 | |

323 | struct TCBPoint { |

324 | QPointF _point; |

325 | qreal _t; |

326 | qreal _c; |

327 | qreal _b; |

328 | |

329 | TCBPoint() {} |

330 | TCBPoint(QPointF point, qreal t, qreal c, qreal b) : _point(point), _t(t), _c(c), _b(b) {} |

331 | |

332 | bool operator==(const TCBPoint &other) const |

333 | { |

334 | return _point == other._point && |

335 | qFuzzyCompare(_t, other._t) && |

336 | qFuzzyCompare(_c, other._c) && |

337 | qFuzzyCompare(_b, other._b); |

338 | } |

339 | }; |

340 | Q_DECLARE_TYPEINFO(TCBPoint, Q_PRIMITIVE_TYPE); |

341 | |

342 | QDataStream &operator<<(QDataStream &stream, const TCBPoint &point) |

343 | { |

344 | stream << point._point |

345 | << point._t |

346 | << point._c |

347 | << point._b; |

348 | return stream; |

349 | } |

350 | |

351 | QDataStream &operator>>(QDataStream &stream, TCBPoint &point) |

352 | { |

353 | stream >> point._point |

354 | >> point._t |

355 | >> point._c |

356 | >> point._b; |

357 | return stream; |

358 | } |

359 | |

360 | typedef QVector<TCBPoint> TCBPoints; |

361 | |

362 | class QEasingCurveFunction |

363 | { |

364 | public: |

365 | QEasingCurveFunction(QEasingCurve::Type type, qreal period = 0.3, qreal amplitude = 1.0, |

366 | qreal overshoot = 1.70158) |

367 | : _t(type), _p(period), _a(amplitude), _o(overshoot) |

368 | { } |

369 | virtual ~QEasingCurveFunction() {} |

370 | virtual qreal value(qreal t); |

371 | virtual QEasingCurveFunction *copy() const; |

372 | bool operator==(const QEasingCurveFunction &other) const; |

373 | |

374 | QEasingCurve::Type _t; |

375 | qreal _p; |

376 | qreal _a; |

377 | qreal _o; |

378 | QVector<QPointF> _bezierCurves; |

379 | TCBPoints _tcbPoints; |

380 | |

381 | }; |

382 | |

383 | QDataStream &operator<<(QDataStream &stream, QEasingCurveFunction *func) |

384 | { |

385 | if (func) { |

386 | stream << func->_p; |

387 | stream << func->_a; |

388 | stream << func->_o; |

389 | if (stream.version() > QDataStream::Qt_5_12) { |

390 | stream << func->_bezierCurves; |

391 | stream << func->_tcbPoints; |

392 | } |

393 | } |

394 | return stream; |

395 | } |

396 | |

397 | QDataStream &operator>>(QDataStream &stream, QEasingCurveFunction *func) |

398 | { |

399 | if (func) { |

400 | stream >> func->_p; |

401 | stream >> func->_a; |

402 | stream >> func->_o; |

403 | if (stream.version() > QDataStream::Qt_5_12) { |

404 | stream >> func->_bezierCurves; |

405 | stream >> func->_tcbPoints; |

406 | } |

407 | } |

408 | return stream; |

409 | } |

410 | |

411 | static QEasingCurve::EasingFunction curveToFunc(QEasingCurve::Type curve); |

412 | |

413 | qreal QEasingCurveFunction::value(qreal t) |

414 | { |

415 | QEasingCurve::EasingFunction func = curveToFunc(_t); |

416 | return func(t); |

417 | } |

418 | |

419 | QEasingCurveFunction *QEasingCurveFunction::copy() const |

420 | { |

421 | QEasingCurveFunction *rv = new QEasingCurveFunction(_t, _p, _a, _o); |

422 | rv->_bezierCurves = _bezierCurves; |

423 | rv->_tcbPoints = _tcbPoints; |

424 | return rv; |

425 | } |

426 | |

427 | bool QEasingCurveFunction::operator==(const QEasingCurveFunction &other) const |

428 | { |

429 | return _t == other._t && |

430 | qFuzzyCompare(_p, other._p) && |

431 | qFuzzyCompare(_a, other._a) && |

432 | qFuzzyCompare(_o, other._o) && |

433 | _bezierCurves == other._bezierCurves && |

434 | _tcbPoints == other._tcbPoints; |

435 | } |

436 | |

437 | QT_BEGIN_INCLUDE_NAMESPACE |

438 | #include "../../3rdparty/easing/easing.cpp" |

439 | QT_END_INCLUDE_NAMESPACE |

440 | |

441 | class QEasingCurvePrivate |

442 | { |

443 | public: |

444 | QEasingCurvePrivate() |

445 | : type(QEasingCurve::Linear), |

446 | config(0), |

447 | func(&easeNone) |

448 | { } |

449 | QEasingCurvePrivate(const QEasingCurvePrivate &other) |

450 | : type(other.type), |

451 | config(other.config ? other.config->copy() : 0), |

452 | func(other.func) |

453 | { } |

454 | ~QEasingCurvePrivate() { delete config; } |

455 | void setType_helper(QEasingCurve::Type); |

456 | |

457 | QEasingCurve::Type type; |

458 | QEasingCurveFunction *config; |

459 | QEasingCurve::EasingFunction func; |

460 | }; |

461 | |

462 | struct BezierEase : public QEasingCurveFunction |

463 | { |

464 | struct SingleCubicBezier { |

465 | qreal p0x, p0y; |

466 | qreal p1x, p1y; |

467 | qreal p2x, p2y; |

468 | qreal p3x, p3y; |

469 | }; |

470 | |

471 | QVector<SingleCubicBezier> _curves; |

472 | QVector<qreal> _intervals; |

473 | int _curveCount; |

474 | bool _init; |

475 | bool _valid; |

476 | |

477 | BezierEase(QEasingCurve::Type type = QEasingCurve::BezierSpline) |

478 | : QEasingCurveFunction(type), _curves(10), _intervals(10), _init(false), _valid(false) |

479 | { } |

480 | |

481 | void init() |

482 | { |

483 | if (_bezierCurves.constLast() == QPointF(1.0, 1.0)) { |

484 | _init = true; |

485 | _curveCount = _bezierCurves.count() / 3; |

486 | |

487 | for (int i=0; i < _curveCount; i++) { |

488 | _intervals[i] = _bezierCurves.at(i * 3 + 2).x(); |

489 | |

490 | if (i == 0) { |

491 | _curves[0].p0x = 0.0; |

492 | _curves[0].p0y = 0.0; |

493 | |

494 | _curves[0].p1x = _bezierCurves.at(0).x(); |

495 | _curves[0].p1y = _bezierCurves.at(0).y(); |

496 | |

497 | _curves[0].p2x = _bezierCurves.at(1).x(); |

498 | _curves[0].p2y = _bezierCurves.at(1).y(); |

499 | |

500 | _curves[0].p3x = _bezierCurves.at(2).x(); |

501 | _curves[0].p3y = _bezierCurves.at(2).y(); |

502 | |

503 | } else if (i == (_curveCount - 1)) { |

504 | _curves[i].p0x = _bezierCurves.at(_bezierCurves.count() - 4).x(); |

505 | _curves[i].p0y = _bezierCurves.at(_bezierCurves.count() - 4).y(); |

506 | |

507 | _curves[i].p1x = _bezierCurves.at(_bezierCurves.count() - 3).x(); |

508 | _curves[i].p1y = _bezierCurves.at(_bezierCurves.count() - 3).y(); |

509 | |

510 | _curves[i].p2x = _bezierCurves.at(_bezierCurves.count() - 2).x(); |

511 | _curves[i].p2y = _bezierCurves.at(_bezierCurves.count() - 2).y(); |

512 | |

513 | _curves[i].p3x = _bezierCurves.at(_bezierCurves.count() - 1).x(); |

514 | _curves[i].p3y = _bezierCurves.at(_bezierCurves.count() - 1).y(); |

515 | } else { |

516 | _curves[i].p0x = _bezierCurves.at(i * 3 - 1).x(); |

517 | _curves[i].p0y = _bezierCurves.at(i * 3 - 1).y(); |

518 | |

519 | _curves[i].p1x = _bezierCurves.at(i * 3).x(); |

520 | _curves[i].p1y = _bezierCurves.at(i * 3).y(); |

521 | |

522 | _curves[i].p2x = _bezierCurves.at(i * 3 + 1).x(); |

523 | _curves[i].p2y = _bezierCurves.at(i * 3 + 1).y(); |

524 | |

525 | _curves[i].p3x = _bezierCurves.at(i * 3 + 2).x(); |

526 | _curves[i].p3y = _bezierCurves.at(i * 3 + 2).y(); |

527 | } |

528 | } |

529 | _valid = true; |

530 | } else { |

531 | _valid = false; |

532 | } |

533 | } |

534 | |

535 | QEasingCurveFunction *copy() const override |

536 | { |

537 | BezierEase *rv = new BezierEase(); |

538 | rv->_t = _t; |

539 | rv->_p = _p; |

540 | rv->_a = _a; |

541 | rv->_o = _o; |

542 | rv->_bezierCurves = _bezierCurves; |

543 | rv->_tcbPoints = _tcbPoints; |

544 | return rv; |

545 | } |

546 | |

547 | void getBezierSegment(SingleCubicBezier * &singleCubicBezier, qreal x) |

548 | { |

549 | |

550 | int currentSegment = 0; |

551 | |

552 | while (currentSegment < _curveCount) { |

553 | if (x <= _intervals.data()[currentSegment]) |

554 | break; |

555 | currentSegment++; |

556 | } |

557 | |

558 | singleCubicBezier = &_curves.data()[currentSegment]; |

559 | } |

560 | |

561 | |

562 | qreal static inline newtonIteration(const SingleCubicBezier &singleCubicBezier, qreal t, qreal x) |

563 | { |

564 | qreal currentXValue = evaluateForX(singleCubicBezier, t); |

565 | |

566 | const qreal newT = t - (currentXValue - x) / evaluateDerivateForX(singleCubicBezier, t); |

567 | |

568 | return newT; |

569 | } |

570 | |

571 | qreal value(qreal x) override |

572 | { |

573 | Q_ASSERT(_bezierCurves.count() % 3 == 0); |

574 | |

575 | if (_bezierCurves.isEmpty()) { |

576 | return x; |

577 | } |

578 | |

579 | if (!_init) |

580 | init(); |

581 | |

582 | if (!_valid) { |

583 | qWarning("QEasingCurve: Invalid bezier curve"); |

584 | return x; |

585 | } |

586 | SingleCubicBezier *singleCubicBezier = 0; |

587 | getBezierSegment(singleCubicBezier, x); |

588 | |

589 | return evaluateSegmentForY(*singleCubicBezier, findTForX(*singleCubicBezier, x)); |

590 | } |

591 | |

592 | qreal static inline evaluateSegmentForY(const SingleCubicBezier &singleCubicBezier, qreal t) |

593 | { |

594 | const qreal p0 = singleCubicBezier.p0y; |

595 | const qreal p1 = singleCubicBezier.p1y; |

596 | const qreal p2 = singleCubicBezier.p2y; |

597 | const qreal p3 = singleCubicBezier.p3y; |

598 | |

599 | const qreal s = 1 - t; |

600 | |

601 | const qreal s_squared = s*s; |

602 | const qreal t_squared = t*t; |

603 | |

604 | const qreal s_cubic = s_squared * s; |

605 | const qreal t_cubic = t_squared * t; |

606 | |

607 | return s_cubic * p0 + 3 * s_squared * t * p1 + 3 * s * t_squared * p2 + t_cubic * p3; |

608 | } |

609 | |

610 | qreal static inline evaluateForX(const SingleCubicBezier &singleCubicBezier, qreal t) |

611 | { |

612 | const qreal p0 = singleCubicBezier.p0x; |

613 | const qreal p1 = singleCubicBezier.p1x; |

614 | const qreal p2 = singleCubicBezier.p2x; |

615 | const qreal p3 = singleCubicBezier.p3x; |

616 | |

617 | const qreal s = 1 - t; |

618 | |

619 | const qreal s_squared = s*s; |

620 | const qreal t_squared = t*t; |

621 | |

622 | const qreal s_cubic = s_squared * s; |

623 | const qreal t_cubic = t_squared * t; |

624 | |

625 | return s_cubic * p0 + 3 * s_squared * t * p1 + 3 * s * t_squared * p2 + t_cubic * p3; |

626 | } |

627 | |

628 | qreal static inline evaluateDerivateForX(const SingleCubicBezier &singleCubicBezier, qreal t) |

629 | { |

630 | const qreal p0 = singleCubicBezier.p0x; |

631 | const qreal p1 = singleCubicBezier.p1x; |

632 | const qreal p2 = singleCubicBezier.p2x; |

633 | const qreal p3 = singleCubicBezier.p3x; |

634 | |

635 | const qreal t_squared = t*t; |

636 | |

637 | return -3*p0 + 3*p1 + 6*p0*t - 12*p1*t + 6*p2*t + 3*p3*t_squared - 3*p0*t_squared + 9*p1*t_squared - 9*p2*t_squared; |

638 | } |

639 | |

640 | qreal static inline _cbrt(qreal d) |

641 | { |

642 | qreal sign = 1; |

643 | if (d < 0) |

644 | sign = -1; |

645 | d = d * sign; |

646 | |

647 | qreal t = _fast_cbrt(d); |

648 | |

649 | //one step of Halley's Method to get a better approximation |

650 | const qreal t_cubic = t * t * t; |

651 | const qreal f = t_cubic + t_cubic + d; |

652 | if (f != qreal(0.0)) |

653 | t = t * (t_cubic + d + d) / f; |

654 | |

655 | //another step |

656 | /*qreal t_i = t; |

657 | t_i_cubic = pow(t_i, 3); |

658 | t = t_i * (t_i_cubic + d + d) / (t_i_cubic + t_i_cubic + d);*/ |

659 | |

660 | return t * sign; |

661 | } |

662 | |

663 | float static inline _fast_cbrt(float x) |

664 | { |

665 | union { |

666 | float f; |

667 | quint32 i; |

668 | } ux; |

669 | |

670 | const unsigned int B1 = 709921077; |

671 | |

672 | ux.f = x; |

673 | ux.i = (ux.i / 3 + B1); |

674 | |

675 | return ux.f; |

676 | } |

677 | |

678 | double static inline _fast_cbrt(double d) |

679 | { |

680 | union { |

681 | double d; |

682 | quint32 pt[2]; |

683 | } ut, ux; |

684 | |

685 | const unsigned int B1 = 715094163; |

686 | |

687 | #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN |

688 | const int h0 = 1; |

689 | #else |

690 | const int h0 = 0; |

691 | #endif |

692 | ut.d = 0.0; |

693 | ux.d = d; |

694 | |

695 | quint32 hx = ux.pt[h0]; //high word of d |

696 | ut.pt[h0] = hx / 3 + B1; |

697 | |

698 | return ut.d; |

699 | } |

700 | |

701 | qreal static inline _acos(qreal x) |

702 | { |

703 | return std::sqrt(1-x)*(1.5707963267948966192313216916398f + x*(-0.213300989f + x*(0.077980478f + x*-0.02164095f))); |

704 | } |

705 | |

706 | qreal static inline _cos(qreal x) //super fast _cos |

707 | { |

708 | const qreal pi_times2 = 2 * M_PI; |

709 | const qreal pi_neg = -1 * M_PI; |

710 | const qreal pi_by2 = M_PI / 2.0; |

711 | |

712 | x += pi_by2; //the polynom is for sin |

713 | |

714 | if (x < pi_neg) |

715 | x += pi_times2; |

716 | else if (x > M_PI) |

717 | x -= pi_times2; |

718 | |

719 | const qreal a = 0.405284735; |

720 | const qreal b = 1.27323954; |

721 | |

722 | const qreal x_squared = x * x; |

723 | |

724 | if (x < 0) { |

725 | qreal cos = b * x + a * x_squared; |

726 | |

727 | if (cos < 0) |

728 | return 0.225 * (cos * -1 * cos - cos) + cos; |

729 | return 0.225 * (cos * cos - cos) + cos; |

730 | } //else |

731 | |

732 | qreal cos = b * x - a * x_squared; |

733 | |

734 | if (cos < 0) |

735 | return 0.225 * (cos * 1 *-cos - cos) + cos; |

736 | return 0.225 * (cos * cos - cos) + cos; |

737 | } |

738 | |

739 | bool static inline inRange(qreal f) |

740 | { |

741 | return (f >= -0.01 && f <= 1.01); |

742 | } |

743 | |

744 | void static inline cosacos(qreal x, qreal &s1, qreal &s2, qreal &s3 ) |

745 | { |

746 | //This function has no proper algebraic representation in real numbers. |

747 | //We use approximations instead |

748 | |

749 | const qreal x_squared = x * x; |

750 | const qreal x_plus_one_sqrt = qSqrt(1.0 + x); |

751 | const qreal one_minus_x_sqrt = qSqrt(1.0 - x); |

752 | |

753 | //cos(acos(x) / 3) |

754 | //s1 = _cos(_acos(x) / 3); |

755 | s1 = 0.463614 - 0.0347815 * x + 0.00218245 * x_squared + 0.402421 * x_plus_one_sqrt; |

756 | |

757 | //cos(acos((x) - M_PI) / 3) |

758 | //s3 = _cos((_acos(x) - M_PI) / 3); |

759 | s3 = 0.463614 + 0.402421 * one_minus_x_sqrt + 0.0347815 * x + 0.00218245 * x_squared; |

760 | |

761 | //cos((acos(x) + M_PI) / 3) |

762 | //s2 = _cos((_acos(x) + M_PI) / 3); |

763 | s2 = -0.401644 * one_minus_x_sqrt - 0.0686804 * x + 0.401644 * x_plus_one_sqrt; |

764 | } |

765 | |

766 | qreal static inline singleRealSolutionForCubic(qreal a, qreal b, qreal c) |

767 | { |

768 | //returns the real solutiuon in [0..1] |

769 | //We use the Cardano formula |

770 | |

771 | //substituiton: x = z - a/3 |

772 | // z^3+pz+q=0 |

773 | |

774 | if (c < 0.000001 && c > -0.000001) |

775 | return 0; |

776 | |

777 | const qreal a_by3 = a / 3.0; |

778 | |

779 | const qreal a_cubic = a * a * a; |

780 | |

781 | const qreal p = b - a * a_by3; |

782 | const qreal q = 2.0 * a_cubic / 27.0 - a * b / 3.0 + c; |

783 | |

784 | const qreal q_squared = q * q; |

785 | const qreal p_cubic = p * p * p; |

786 | const qreal D = 0.25 * q_squared + p_cubic / 27.0; |

787 | |

788 | if (D >= 0) { |

789 | const qreal D_sqrt = qSqrt(D); |

790 | qreal u = _cbrt( -q * 0.5 + D_sqrt); |

791 | qreal v = _cbrt( -q * 0.5 - D_sqrt); |

792 | qreal z1 = u + v; |

793 | |

794 | qreal t1 = z1 - a_by3; |

795 | |

796 | if (inRange(t1)) |

797 | return t1; |

798 | qreal z2 = -1 *u; |

799 | qreal t2 = z2 - a_by3; |

800 | return t2; |

801 | } |

802 | |

803 | //casus irreducibilis |

804 | const qreal p_minus_sqrt = qSqrt(-p); |

805 | |

806 | //const qreal f = sqrt(4.0 / 3.0 * -p); |

807 | const qreal f = qSqrt(4.0 / 3.0) * p_minus_sqrt; |

808 | |

809 | //const qreal sqrtP = sqrt(27.0 / -p_cubic); |

810 | const qreal sqrtP = -3.0*qSqrt(3.0) / (p_minus_sqrt * p); |

811 | |

812 | |

813 | const qreal g = -q * 0.5 * sqrtP; |

814 | |

815 | qreal s1; |

816 | qreal s2; |

817 | qreal s3; |

818 | |

819 | cosacos(g, s1, s2, s3); |

820 | |

821 | qreal z1 = -1* f * s2; |

822 | qreal t1 = z1 - a_by3; |

823 | if (inRange(t1)) |

824 | return t1; |

825 | |

826 | qreal z2 = f * s1; |

827 | qreal t2 = z2 - a_by3; |

828 | if (inRange(t2)) |

829 | return t2; |

830 | |

831 | qreal z3 = -1 * f * s3; |

832 | qreal t3 = z3 - a_by3; |

833 | return t3; |

834 | } |

835 | |

836 | bool static inline almostZero(qreal value) |

837 | { |

838 | // 1e-3 might seem excessively fuzzy, but any smaller value will make the |

839 | // factors a, b, and c large enough to knock out the cubic solver. |

840 | return value > -1e-3 && value < 1e-3; |

841 | } |

842 | |

843 | qreal static inline findTForX(const SingleCubicBezier &singleCubicBezier, qreal x) |

844 | { |

845 | const qreal p0 = singleCubicBezier.p0x; |

846 | const qreal p1 = singleCubicBezier.p1x; |

847 | const qreal p2 = singleCubicBezier.p2x; |

848 | const qreal p3 = singleCubicBezier.p3x; |

849 | |

850 | const qreal factorT3 = p3 - p0 + 3 * p1 - 3 * p2; |

851 | const qreal factorT2 = 3 * p0 - 6 * p1 + 3 * p2; |

852 | const qreal factorT1 = -3 * p0 + 3 * p1; |

853 | const qreal factorT0 = p0 - x; |

854 | |

855 | // Cases for quadratic, linear and invalid equations |

856 | if (almostZero(factorT3)) { |

857 | if (almostZero(factorT2)) { |

858 | if (almostZero(factorT1)) |

859 | return 0.0; |

860 | |

861 | return -factorT0 / factorT1; |

862 | } |

863 | const qreal discriminant = factorT1 * factorT1 - 4.0 * factorT2 * factorT0; |

864 | if (discriminant < 0.0) |

865 | return 0.0; |

866 | |

867 | if (discriminant == 0.0) |

868 | return -factorT1 / (2.0 * factorT2); |

869 | |

870 | const qreal solution1 = (-factorT1 + std::sqrt(discriminant)) / (2.0 * factorT2); |

871 | if (solution1 >= 0.0 && solution1 <= 1.0) |

872 | return solution1; |

873 | |

874 | const qreal solution2 = (-factorT1 - std::sqrt(discriminant)) / (2.0 * factorT2); |

875 | if (solution2 >= 0.0 && solution2 <= 1.0) |

876 | return solution2; |

877 | |

878 | return 0.0; |

879 | } |

880 | |

881 | const qreal a = factorT2 / factorT3; |

882 | const qreal b = factorT1 / factorT3; |

883 | const qreal c = factorT0 / factorT3; |

884 | |

885 | return singleRealSolutionForCubic(a, b, c); |

886 | |

887 | //one new iteration to increase numeric stability |

888 | //return newtonIteration(singleCubicBezier, t, x); |

889 | } |

890 | }; |

891 | |

892 | struct TCBEase : public BezierEase |

893 | { |

894 | TCBEase() |

895 | : BezierEase(QEasingCurve::TCBSpline) |

896 | { } |

897 | |

898 | qreal value(qreal x) override |

899 | { |

900 | Q_ASSERT(_bezierCurves.count() % 3 == 0); |

901 | |

902 | if (_bezierCurves.isEmpty()) { |

903 | qWarning("QEasingCurve: Invalid tcb curve"); |

904 | return x; |

905 | } |

906 | |

907 | return BezierEase::value(x); |

908 | } |

909 | |

910 | QEasingCurveFunction *copy() const override |

911 | { |

912 | return new TCBEase{*this}; |

913 | } |

914 | }; |

915 | |

916 | struct ElasticEase : public QEasingCurveFunction |

917 | { |

918 | ElasticEase(QEasingCurve::Type type) |

919 | : QEasingCurveFunction(type, qreal(0.3), qreal(1.0)) |

920 | { } |

921 | |

922 | QEasingCurveFunction *copy() const override |

923 | { |

924 | ElasticEase *rv = new ElasticEase(_t); |

925 | rv->_p = _p; |

926 | rv->_a = _a; |

927 | rv->_bezierCurves = _bezierCurves; |

928 | rv->_tcbPoints = _tcbPoints; |

929 | return rv; |

930 | } |

931 | |

932 | qreal value(qreal t) override |

933 | { |

934 | qreal p = (_p < 0) ? qreal(0.3) : _p; |

935 | qreal a = (_a < 0) ? qreal(1.0) : _a; |

936 | switch(_t) { |

937 | case QEasingCurve::InElastic: |

938 | return easeInElastic(t, a, p); |

939 | case QEasingCurve::OutElastic: |

940 | return easeOutElastic(t, a, p); |

941 | case QEasingCurve::InOutElastic: |

942 | return easeInOutElastic(t, a, p); |

943 | case QEasingCurve::OutInElastic: |

944 | return easeOutInElastic(t, a, p); |

945 | default: |

946 | return t; |

947 | } |

948 | } |

949 | }; |

950 | |

951 | struct BounceEase : public QEasingCurveFunction |

952 | { |

953 | BounceEase(QEasingCurve::Type type) |

954 | : QEasingCurveFunction(type, qreal(0.3), qreal(1.0)) |

955 | { } |

956 | |

957 | QEasingCurveFunction *copy() const override |

958 | { |

959 | BounceEase *rv = new BounceEase(_t); |

960 | rv->_a = _a; |

961 | rv->_bezierCurves = _bezierCurves; |

962 | rv->_tcbPoints = _tcbPoints; |

963 | return rv; |

964 | } |

965 | |

966 | qreal value(qreal t) override |

967 | { |

968 | qreal a = (_a < 0) ? qreal(1.0) : _a; |

969 | switch(_t) { |

970 | case QEasingCurve::InBounce: |

971 | return easeInBounce(t, a); |

972 | case QEasingCurve::OutBounce: |

973 | return easeOutBounce(t, a); |

974 | case QEasingCurve::InOutBounce: |

975 | return easeInOutBounce(t, a); |

976 | case QEasingCurve::OutInBounce: |

977 | return easeOutInBounce(t, a); |

978 | default: |

979 | return t; |

980 | } |

981 | } |

982 | }; |

983 | |

984 | struct BackEase : public QEasingCurveFunction |

985 | { |

986 | BackEase(QEasingCurve::Type type) |

987 | : QEasingCurveFunction(type, qreal(0.3), qreal(1.0), qreal(1.70158)) |

988 | { } |

989 | |

990 | QEasingCurveFunction *copy() const override |

991 | { |

992 | BackEase *rv = new BackEase(_t); |

993 | rv->_o = _o; |

994 | rv->_bezierCurves = _bezierCurves; |

995 | rv->_tcbPoints = _tcbPoints; |

996 | return rv; |

997 | } |

998 | |

999 | qreal value(qreal t) override |

1000 | { |

1001 | qreal o = (_o < 0) ? qreal(1.70158) : _o; |

1002 | switch(_t) { |

1003 | case QEasingCurve::InBack: |

1004 | return easeInBack(t, o); |

1005 | case QEasingCurve::OutBack: |

1006 | return easeOutBack(t, o); |

1007 | case QEasingCurve::InOutBack: |

1008 | return easeInOutBack(t, o); |

1009 | case QEasingCurve::OutInBack: |

1010 | return easeOutInBack(t, o); |

1011 | default: |

1012 | return t; |

1013 | } |

1014 | } |

1015 | }; |

1016 | |

1017 | static QEasingCurve::EasingFunction curveToFunc(QEasingCurve::Type curve) |

1018 | { |

1019 | switch(curve) { |

1020 | case QEasingCurve::Linear: |

1021 | return &easeNone; |

1022 | case QEasingCurve::InQuad: |

1023 | return &easeInQuad; |

1024 | case QEasingCurve::OutQuad: |

1025 | return &easeOutQuad; |

1026 | case QEasingCurve::InOutQuad: |

1027 | return &easeInOutQuad; |

1028 | case QEasingCurve::OutInQuad: |

1029 | return &easeOutInQuad; |

1030 | case QEasingCurve::InCubic: |

1031 | return &easeInCubic; |

1032 | case QEasingCurve::OutCubic: |

1033 | return &easeOutCubic; |

1034 | case QEasingCurve::InOutCubic: |

1035 | return &easeInOutCubic; |

1036 | case QEasingCurve::OutInCubic: |

1037 | return &easeOutInCubic; |

1038 | case QEasingCurve::InQuart: |

1039 | return &easeInQuart; |

1040 | case QEasingCurve::OutQuart: |

1041 | return &easeOutQuart; |

1042 | case QEasingCurve::InOutQuart: |

1043 | return &easeInOutQuart; |

1044 | case QEasingCurve::OutInQuart: |

1045 | return &easeOutInQuart; |

1046 | case QEasingCurve::InQuint: |

1047 | return &easeInQuint; |

1048 | case QEasingCurve::OutQuint: |

1049 | return &easeOutQuint; |

1050 | case QEasingCurve::InOutQuint: |

1051 | return &easeInOutQuint; |

1052 | case QEasingCurve::OutInQuint: |

1053 | return &easeOutInQuint; |

1054 | case QEasingCurve::InSine: |

1055 | return &easeInSine; |

1056 | case QEasingCurve::OutSine: |

1057 | return &easeOutSine; |

1058 | case QEasingCurve::InOutSine: |

1059 | return &easeInOutSine; |

1060 | case QEasingCurve::OutInSine: |

1061 | return &easeOutInSine; |

1062 | case QEasingCurve::InExpo: |

1063 | return &easeInExpo; |

1064 | case QEasingCurve::OutExpo: |

1065 | return &easeOutExpo; |

1066 | case QEasingCurve::InOutExpo: |

1067 | return &easeInOutExpo; |

1068 | case QEasingCurve::OutInExpo: |

1069 | return &easeOutInExpo; |

1070 | case QEasingCurve::InCirc: |

1071 | return &easeInCirc; |

1072 | case QEasingCurve::OutCirc: |

1073 | return &easeOutCirc; |

1074 | case QEasingCurve::InOutCirc: |

1075 | return &easeInOutCirc; |

1076 | case QEasingCurve::OutInCirc: |

1077 | return &easeOutInCirc; |

1078 | // Internal for, compatibility with QTimeLine only ?? |

1079 | case QEasingCurve::InCurve: |

1080 | return &easeInCurve; |

1081 | case QEasingCurve::OutCurve: |

1082 | return &easeOutCurve; |

1083 | case QEasingCurve::SineCurve: |

1084 | return &easeSineCurve; |

1085 | case QEasingCurve::CosineCurve: |

1086 | return &easeCosineCurve; |

1087 | default: |

1088 | return 0; |

1089 | }; |

1090 | } |

1091 | |

1092 | static QEasingCurveFunction *curveToFunctionObject(QEasingCurve::Type type) |

1093 | { |

1094 | switch(type) { |

1095 | case QEasingCurve::InElastic: |

1096 | case QEasingCurve::OutElastic: |

1097 | case QEasingCurve::InOutElastic: |

1098 | case QEasingCurve::OutInElastic: |

1099 | return new ElasticEase(type); |

1100 | case QEasingCurve::OutBounce: |

1101 | case QEasingCurve::InBounce: |

1102 | case QEasingCurve::OutInBounce: |

1103 | case QEasingCurve::InOutBounce: |

1104 | return new BounceEase(type); |

1105 | case QEasingCurve::InBack: |

1106 | case QEasingCurve::OutBack: |

1107 | case QEasingCurve::InOutBack: |

1108 | case QEasingCurve::OutInBack: |

1109 | return new BackEase(type); |

1110 | case QEasingCurve::BezierSpline: |

1111 | return new BezierEase; |

1112 | case QEasingCurve::TCBSpline: |

1113 | return new TCBEase; |

1114 | default: |

1115 | return new QEasingCurveFunction(type, qreal(0.3), qreal(1.0), qreal(1.70158)); |

1116 | } |

1117 | |

1118 | return 0; |

1119 | } |

1120 | |

1121 | /*! |

1122 | \fn QEasingCurve::QEasingCurve(QEasingCurve &&other) |

1123 | |

1124 | Move-constructs a QEasingCurve instance, making it point at the same |

1125 | object that \a other was pointing to. |

1126 | |

1127 | \since 5.2 |

1128 | */ |

1129 | |

1130 | /*! |

1131 | Constructs an easing curve of the given \a type. |

1132 | */ |

1133 | QEasingCurve::QEasingCurve(Type type) |

1134 | : d_ptr(new QEasingCurvePrivate) |

1135 | { |

1136 | setType(type); |

1137 | } |

1138 | |

1139 | /*! |

1140 | Construct a copy of \a other. |

1141 | */ |

1142 | QEasingCurve::QEasingCurve(const QEasingCurve &other) |

1143 | : d_ptr(new QEasingCurvePrivate(*other.d_ptr)) |

1144 | { |

1145 | // ### non-atomic, requires malloc on shallow copy |

1146 | } |

1147 | |

1148 | /*! |

1149 | Destructor. |

1150 | */ |

1151 | |

1152 | QEasingCurve::~QEasingCurve() |

1153 | { |

1154 | delete d_ptr; |

1155 | } |

1156 | |

1157 | /*! |

1158 | \fn QEasingCurve &QEasingCurve::operator=(const QEasingCurve &other) |

1159 | Copy \a other. |

1160 | */ |

1161 | |

1162 | /*! |

1163 | \fn QEasingCurve &QEasingCurve::operator=(QEasingCurve &&other) |

1164 | |

1165 | Move-assigns \a other to this QEasingCurve instance. |

1166 | |

1167 | \since 5.2 |

1168 | */ |

1169 | |

1170 | /*! |

1171 | \fn void QEasingCurve::swap(QEasingCurve &other) |

1172 | \since 5.0 |

1173 | |

1174 | Swaps curve \a other with this curve. This operation is very |

1175 | fast and never fails. |

1176 | */ |

1177 | |

1178 | /*! |

1179 | Compare this easing curve with \a other and returns \c true if they are |

1180 | equal. It will also compare the properties of a curve. |

1181 | */ |

1182 | bool QEasingCurve::operator==(const QEasingCurve &other) const |

1183 | { |

1184 | bool res = d_ptr->func == other.d_ptr->func |

1185 | && d_ptr->type == other.d_ptr->type; |

1186 | if (res) { |

1187 | if (d_ptr->config && other.d_ptr->config) { |

1188 | // catch the config content |

1189 | res = d_ptr->config->operator==(*(other.d_ptr->config)); |

1190 | |

1191 | } else if (d_ptr->config || other.d_ptr->config) { |

1192 | // one one has a config object, which could contain default values |

1193 | res = qFuzzyCompare(amplitude(), other.amplitude()) && |

1194 | qFuzzyCompare(period(), other.period()) && |

1195 | qFuzzyCompare(overshoot(), other.overshoot()); |

1196 | } |

1197 | } |

1198 | return res; |

1199 | } |

1200 | |

1201 | /*! |

1202 | \fn bool QEasingCurve::operator!=(const QEasingCurve &other) const |

1203 | Compare this easing curve with \a other and returns \c true if they are not equal. |

1204 | It will also compare the properties of a curve. |

1205 | |

1206 | \sa operator==() |

1207 | */ |

1208 | |

1209 | /*! |

1210 | Returns the amplitude. This is not applicable for all curve types. |

1211 | It is only applicable for bounce and elastic curves (curves of type() |

1212 | QEasingCurve::InBounce, QEasingCurve::OutBounce, QEasingCurve::InOutBounce, |

1213 | QEasingCurve::OutInBounce, QEasingCurve::InElastic, QEasingCurve::OutElastic, |

1214 | QEasingCurve::InOutElastic or QEasingCurve::OutInElastic). |

1215 | */ |

1216 | qreal QEasingCurve::amplitude() const |

1217 | { |

1218 | return d_ptr->config ? d_ptr->config->_a : qreal(1.0); |

1219 | } |

1220 | |

1221 | /*! |

1222 | Sets the amplitude to \a amplitude. |

1223 | |

1224 | This will set the amplitude of the bounce or the amplitude of the |

1225 | elastic "spring" effect. The higher the number, the higher the amplitude. |

1226 | \sa amplitude() |

1227 | */ |

1228 | void QEasingCurve::setAmplitude(qreal amplitude) |

1229 | { |

1230 | if (!d_ptr->config) |

1231 | d_ptr->config = curveToFunctionObject(d_ptr->type); |

1232 | d_ptr->config->_a = amplitude; |

1233 | } |

1234 | |

1235 | /*! |

1236 | Returns the period. This is not applicable for all curve types. |

1237 | It is only applicable if type() is QEasingCurve::InElastic, QEasingCurve::OutElastic, |

1238 | QEasingCurve::InOutElastic or QEasingCurve::OutInElastic. |

1239 | */ |

1240 | qreal QEasingCurve::period() const |

1241 | { |

1242 | return d_ptr->config ? d_ptr->config->_p : qreal(0.3); |

1243 | } |

1244 | |

1245 | /*! |

1246 | Sets the period to \a period. |

1247 | Setting a small period value will give a high frequency of the curve. A |

1248 | large period will give it a small frequency. |

1249 | |

1250 | \sa period() |

1251 | */ |

1252 | void QEasingCurve::setPeriod(qreal period) |

1253 | { |

1254 | if (!d_ptr->config) |

1255 | d_ptr->config = curveToFunctionObject(d_ptr->type); |

1256 | d_ptr->config->_p = period; |

1257 | } |

1258 | |

1259 | /*! |

1260 | Returns the overshoot. This is not applicable for all curve types. |

1261 | It is only applicable if type() is QEasingCurve::InBack, QEasingCurve::OutBack, |

1262 | QEasingCurve::InOutBack or QEasingCurve::OutInBack. |

1263 | */ |

1264 | qreal QEasingCurve::overshoot() const |

1265 | { |

1266 | return d_ptr->config ? d_ptr->config->_o : qreal(1.70158) ; |

1267 | } |

1268 | |

1269 | /*! |

1270 | Sets the overshoot to \a overshoot. |

1271 | |

1272 | 0 produces no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent. |

1273 | |

1274 | \sa overshoot() |

1275 | */ |

1276 | void QEasingCurve::setOvershoot(qreal overshoot) |

1277 | { |

1278 | if (!d_ptr->config) |

1279 | d_ptr->config = curveToFunctionObject(d_ptr->type); |

1280 | d_ptr->config->_o = overshoot; |

1281 | } |

1282 | |

1283 | /*! |

1284 | Adds a segment of a cubic bezier spline to define a custom easing curve. |

1285 | It is only applicable if type() is QEasingCurve::BezierSpline. |

1286 | Note that the spline implicitly starts at (0.0, 0.0) and has to end at (1.0, 1.0) to |

1287 | be a valid easing curve. |

1288 | \a c1 and \a c2 are the control points used for drawing the curve. |

1289 | \a endPoint is the endpoint of the curve. |

1290 | */ |

1291 | void QEasingCurve::addCubicBezierSegment(const QPointF & c1, const QPointF & c2, const QPointF & endPoint) |

1292 | { |

1293 | if (!d_ptr->config) |

1294 | d_ptr->config = curveToFunctionObject(d_ptr->type); |

1295 | d_ptr->config->_bezierCurves << c1 << c2 << endPoint; |

1296 | } |

1297 | |

1298 | QVector<QPointF> static inline tcbToBezier(const TCBPoints &tcbPoints) |

1299 | { |

1300 | const int count = tcbPoints.count(); |

1301 | QVector<QPointF> bezierPoints; |

1302 | bezierPoints.reserve(3 * (count - 1)); |

1303 | |

1304 | for (int i = 1; i < count; i++) { |

1305 | const qreal t_0 = tcbPoints.at(i - 1)._t; |

1306 | const qreal c_0 = tcbPoints.at(i - 1)._c; |

1307 | qreal b_0 = -1; |

1308 | |

1309 | qreal const t_1 = tcbPoints.at(i)._t; |

1310 | qreal const c_1 = tcbPoints.at(i)._c; |

1311 | qreal b_1 = 1; |

1312 | |

1313 | QPointF c_minusOne; //P1 last segment - not available for the first point |

1314 | const QPointF c0(tcbPoints.at(i - 1)._point); //P0 Hermite/TBC |

1315 | const QPointF c3(tcbPoints.at(i)._point); //P1 Hermite/TBC |

1316 | QPointF c4; //P0 next segment - not available for the last point |

1317 | |

1318 | if (i > 1) { //first point no left tangent |

1319 | c_minusOne = tcbPoints.at(i - 2)._point; |

1320 | b_0 = tcbPoints.at(i - 1)._b; |

1321 | } |

1322 | |

1323 | if (i < (count - 1)) { //last point no right tangent |

1324 | c4 = tcbPoints.at(i + 1)._point; |

1325 | b_1 = tcbPoints.at(i)._b; |

1326 | } |

1327 | |

1328 | const qreal dx_0 = 0.5 * (1-t_0) * ((1 + b_0) * (1 + c_0) * (c0.x() - c_minusOne.x()) + (1- b_0) * (1 - c_0) * (c3.x() - c0.x())); |

1329 | const qreal dy_0 = 0.5 * (1-t_0) * ((1 + b_0) * (1 + c_0) * (c0.y() - c_minusOne.y()) + (1- b_0) * (1 - c_0) * (c3.y() - c0.y())); |

1330 | |

1331 | const qreal dx_1 = 0.5 * (1-t_1) * ((1 + b_1) * (1 - c_1) * (c3.x() - c0.x()) + (1 - b_1) * (1 + c_1) * (c4.x() - c3.x())); |

1332 | const qreal dy_1 = 0.5 * (1-t_1) * ((1 + b_1) * (1 - c_1) * (c3.y() - c0.y()) + (1 - b_1) * (1 + c_1) * (c4.y() - c3.y())); |

1333 | |

1334 | const QPointF d_0 = QPointF(dx_0, dy_0); |

1335 | const QPointF d_1 = QPointF(dx_1, dy_1); |

1336 | |

1337 | QPointF c1 = (3 * c0 + d_0) / 3; |

1338 | QPointF c2 = (3 * c3 - d_1) / 3; |

1339 | bezierPoints << c1 << c2 << c3; |

1340 | } |

1341 | return bezierPoints; |

1342 | } |

1343 | |

1344 | /*! |

1345 | Adds a segment of a TCB bezier spline to define a custom easing curve. |

1346 | It is only applicable if type() is QEasingCurve::TCBSpline. |

1347 | The spline has to start explitly at (0.0, 0.0) and has to end at (1.0, 1.0) to |

1348 | be a valid easing curve. |

1349 | The tension \a t changes the length of the tangent vector. |

1350 | The continuity \a c changes the sharpness in change between the tangents. |

1351 | The bias \a b changes the direction of the tangent vector. |

1352 | \a nextPoint is the sample position. |

1353 | All three parameters are valid between -1 and 1 and define the |

1354 | tangent of the control point. |

1355 | If all three parameters are 0 the resulting spline is a Catmull-Rom spline. |

1356 | The begin and endpoint always have a bias of -1 and 1, since the outer tangent is not defined. |

1357 | */ |

1358 | void QEasingCurve::addTCBSegment(const QPointF &nextPoint, qreal t, qreal c, qreal b) |

1359 | { |

1360 | if (!d_ptr->config) |

1361 | d_ptr->config = curveToFunctionObject(d_ptr->type); |

1362 | |

1363 | d_ptr->config->_tcbPoints.append(TCBPoint(nextPoint, t, c ,b)); |

1364 | |

1365 | if (nextPoint == QPointF(1.0, 1.0)) { |

1366 | d_ptr->config->_bezierCurves = tcbToBezier(d_ptr->config->_tcbPoints); |

1367 | d_ptr->config->_tcbPoints.clear(); |

1368 | } |

1369 | |

1370 | } |

1371 | |

1372 | /*! |

1373 | \fn QList<QPointF> QEasingCurve::cubicBezierSpline() const |

1374 | \obsolete Use toCubicSpline() instead. |

1375 | */ |

1376 | |

1377 | /*! |

1378 | \since 5.0 |

1379 | |

1380 | Returns the cubicBezierSpline that defines a custom easing curve. |

1381 | If the easing curve does not have a custom bezier easing curve the list |

1382 | is empty. |

1383 | */ |

1384 | QVector<QPointF> QEasingCurve::toCubicSpline() const |

1385 | { |

1386 | return d_ptr->config ? d_ptr->config->_bezierCurves : QVector<QPointF>(); |

1387 | } |

1388 | |

1389 | /*! |

1390 | Returns the type of the easing curve. |

1391 | */ |

1392 | QEasingCurve::Type QEasingCurve::type() const |

1393 | { |

1394 | return d_ptr->type; |

1395 | } |

1396 | |

1397 | void QEasingCurvePrivate::setType_helper(QEasingCurve::Type newType) |

1398 | { |

1399 | qreal amp = -1.0; |

1400 | qreal period = -1.0; |

1401 | qreal overshoot = -1.0; |

1402 | QVector<QPointF> bezierCurves; |

1403 | QVector<TCBPoint> tcbPoints; |

1404 | |

1405 | if (config) { |

1406 | amp = config->_a; |

1407 | period = config->_p; |

1408 | overshoot = config->_o; |

1409 | bezierCurves = std::move(config->_bezierCurves); |

1410 | tcbPoints = std::move(config->_tcbPoints); |

1411 | |

1412 | delete config; |

1413 | config = 0; |

1414 | } |

1415 | |

1416 | if (isConfigFunction(newType) || (amp != -1.0) || (period != -1.0) || (overshoot != -1.0) || |

1417 | !bezierCurves.isEmpty()) { |

1418 | config = curveToFunctionObject(newType); |

1419 | if (amp != -1.0) |

1420 | config->_a = amp; |

1421 | if (period != -1.0) |

1422 | config->_p = period; |

1423 | if (overshoot != -1.0) |

1424 | config->_o = overshoot; |

1425 | config->_bezierCurves = std::move(bezierCurves); |

1426 | config->_tcbPoints = std::move(tcbPoints); |

1427 | func = 0; |

1428 | } else if (newType != QEasingCurve::Custom) { |

1429 | func = curveToFunc(newType); |

1430 | } |

1431 | Q_ASSERT((func == 0) == (config != 0)); |

1432 | type = newType; |

1433 | } |

1434 | |

1435 | /*! |

1436 | Sets the type of the easing curve to \a type. |

1437 | */ |

1438 | void QEasingCurve::setType(Type type) |

1439 | { |

1440 | if (d_ptr->type == type) |

1441 | return; |

1442 | if (type < Linear || type >= NCurveTypes - 1) { |

1443 | qWarning("QEasingCurve: Invalid curve type %d", type); |

1444 | return; |

1445 | } |

1446 | |

1447 | d_ptr->setType_helper(type); |

1448 | } |

1449 | |

1450 | /*! |

1451 | Sets a custom easing curve that is defined by the user in the function \a func. |

1452 | The signature of the function is qreal myEasingFunction(qreal progress), |

1453 | where \e progress and the return value are considered to be normalized between 0 and 1. |

1454 | (In some cases the return value can be outside that range) |

1455 | After calling this function type() will return QEasingCurve::Custom. |

1456 | \a func cannot be zero. |

1457 | |

1458 | \sa customType() |

1459 | \sa valueForProgress() |

1460 | */ |

1461 | void QEasingCurve::setCustomType(EasingFunction func) |

1462 | { |

1463 | if (!func) { |

1464 | qWarning("Function pointer must not be null"); |

1465 | return; |

1466 | } |

1467 | d_ptr->func = func; |

1468 | d_ptr->setType_helper(Custom); |

1469 | } |

1470 | |

1471 | /*! |

1472 | Returns the function pointer to the custom easing curve. |

1473 | If type() does not return QEasingCurve::Custom, this function |

1474 | will return 0. |

1475 | */ |

1476 | QEasingCurve::EasingFunction QEasingCurve::customType() const |

1477 | { |

1478 | return d_ptr->type == Custom ? d_ptr->func : 0; |

1479 | } |

1480 | |

1481 | /*! |

1482 | Return the effective progress for the easing curve at \a progress. |

1483 | Whereas \a progress must be between 0 and 1, the returned effective progress |

1484 | can be outside those bounds. For example, QEasingCurve::InBack will |

1485 | return negative values in the beginning of the function. |

1486 | */ |

1487 | qreal QEasingCurve::valueForProgress(qreal progress) const |

1488 | { |

1489 | progress = qBound<qreal>(0, progress, 1); |

1490 | if (d_ptr->func) |

1491 | return d_ptr->func(progress); |

1492 | else if (d_ptr->config) |

1493 | return d_ptr->config->value(progress); |

1494 | else |

1495 | return progress; |

1496 | } |

1497 | |

1498 | #ifndef QT_NO_DEBUG_STREAM |

1499 | QDebug operator<<(QDebug debug, const QEasingCurve &item) |

1500 | { |

1501 | QDebugStateSaver saver(debug); |

1502 | debug << "type:"<< item.d_ptr->type |

1503 | << "func:"<< item.d_ptr->func; |

1504 | if (item.d_ptr->config) { |

1505 | debug << QString::fromLatin1("period:%1").arg(item.d_ptr->config->_p, 0, 'f', 20) |

1506 | << QString::fromLatin1("amp:%1").arg(item.d_ptr->config->_a, 0, 'f', 20) |

1507 | << QString::fromLatin1("overshoot:%1").arg(item.d_ptr->config->_o, 0, 'f', 20); |

1508 | } |

1509 | return debug; |

1510 | } |

1511 | #endif // QT_NO_DEBUG_STREAM |

1512 | |

1513 | #ifndef QT_NO_DATASTREAM |

1514 | /*! |

1515 | \fn QDataStream &operator<<(QDataStream &stream, const QEasingCurve &easing) |

1516 | \relates QEasingCurve |

1517 | |

1518 | Writes the given \a easing curve to the given \a stream and returns a |

1519 | reference to the stream. |

1520 | |

1521 | \sa {Serializing Qt Data Types} |

1522 | */ |

1523 | |

1524 | QDataStream &operator<<(QDataStream &stream, const QEasingCurve &easing) |

1525 | { |

1526 | stream << quint8(easing.d_ptr->type); |

1527 | stream << quint64(quintptr(easing.d_ptr->func)); |

1528 | |

1529 | bool hasConfig = easing.d_ptr->config; |

1530 | stream << hasConfig; |

1531 | if (hasConfig) { |

1532 | stream << easing.d_ptr->config; |

1533 | } |

1534 | return stream; |

1535 | } |

1536 | |

1537 | /*! |

1538 | \fn QDataStream &operator>>(QDataStream &stream, QEasingCurve &easing) |

1539 | \relates QEasingCurve |

1540 | |

1541 | Reads an easing curve from the given \a stream into the given \a |

1542 | easing curve and returns a reference to the stream. |

1543 | |

1544 | \sa {Serializing Qt Data Types} |

1545 | */ |

1546 | |

1547 | QDataStream &operator>>(QDataStream &stream, QEasingCurve &easing) |

1548 | { |

1549 | QEasingCurve::Type type; |

1550 | quint8 int_type; |

1551 | stream >> int_type; |

1552 | type = static_cast<QEasingCurve::Type>(int_type); |

1553 | easing.setType(type); |

1554 | |

1555 | quint64 ptr_func; |

1556 | stream >> ptr_func; |

1557 | easing.d_ptr->func = QEasingCurve::EasingFunction(quintptr(ptr_func)); |

1558 | |

1559 | bool hasConfig; |

1560 | stream >> hasConfig; |

1561 | delete easing.d_ptr->config; |

1562 | easing.d_ptr->config = nullptr; |

1563 | if (hasConfig) { |

1564 | QEasingCurveFunction *config = curveToFunctionObject(type); |

1565 | stream >> config; |

1566 | easing.d_ptr->config = config; |

1567 | } |

1568 | return stream; |

1569 | } |

1570 | #endif // QT_NO_DATASTREAM |

1571 | |

1572 | QT_END_NAMESPACE |

1573 | |

1574 | #include "moc_qeasingcurve.cpp" |

1575 |