Marble

MarblePhysics.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2008 Torsten Rahn <rahn@kde.org>
4//
5
6#include "MarblePhysics.h"
7
8#include "Quaternion.h"
9#include "MarbleAbstractPresenter.h"
10#include "GeoDataLookAt.h"
11#include "MarbleDebug.h"
12#include "GeoDataLineString.h"
13#include "ViewportParams.h"
14
15#include <QTimeLine>
16
17namespace Marble
18{
19
20class MarblePhysicsPrivate {
21public:
22 MarbleAbstractPresenter *const m_presenter;
23
24 GeoDataLookAt m_source;
25
26 GeoDataLookAt m_target;
27
28 FlyToMode m_mode;
29
30 QTimeLine m_timeline;
31
32 qreal m_planetRadius;
33
34 explicit MarblePhysicsPrivate( MarbleAbstractPresenter *presenter )
35 : m_presenter( presenter ),
36 m_mode( Instant ),
37 m_planetRadius( EARTH_RADIUS )
38 {
39 m_timeline.setDuration(2000);
41 }
42
43 qreal suggestedRange(qreal t) const
44 {
45 Q_ASSERT( m_mode == Linear || m_mode == Jump);
46 Q_ASSERT( 0 <= t && t <= 1.0 );
47
48 if (m_mode == Linear) {
49 qreal in = m_source.range();
50 qreal out = m_target.range();
51
52 return in + t * (out-in);
53 }
54 else if (m_mode == Jump) {
55 qreal jumpDuration = m_timeline.duration();
56
57 // Purely cinematic approach to calculate the jump path
58 const qreal totalDistance = m_planetRadius * m_source.coordinates().sphericalDistanceTo(m_target.coordinates());
59 qreal g = qMin(m_source.range(), m_target.range()); // Min altitude
60 qreal k = qMax(m_source.range(), m_target.range()); // Base altitude
61 qreal d = t > 0.5 ? m_source.range() - g : m_target.range() - g; // Base difference
62 qreal c = d * 2 * qAbs(t - 0.5); // Correction factor
63 qreal h = qMin(1000*3000.0, totalDistance / 2.0); // Jump height
64
65 // Parameters for the parabolic function that has the maximum at
66 // the point H ( 0.5 * m_jumpDuration, g + h )
67 qreal a = - h / ( (qreal)( 0.25 * jumpDuration * jumpDuration ) );
68 qreal b = 2.0 * h / (qreal)( 0.5 * jumpDuration );
69
70 qreal x = jumpDuration * t;
71 qreal y = ( a * x + b ) * x + k - c; // Parabolic function
72
73 return y;
74 }
75 else {
76 qWarning("Unhandled FlyTo mode, no camera distance interpolation.");
77 return m_target.range();
78 }
79 }
80};
81
82
83MarblePhysics::MarblePhysics( MarbleAbstractPresenter *presenter )
84 : QObject( presenter ),
85 d( new MarblePhysicsPrivate( presenter ) )
86{
87 connect( &d->m_timeline, SIGNAL(valueChanged(qreal)),
88 this, SLOT(updateProgress(qreal)) );
89 connect( &d->m_timeline, SIGNAL(finished()),
90 this, SLOT(startStillMode()) );
91}
92
93MarblePhysics::~MarblePhysics()
94{
95 delete d;
96}
97
98void MarblePhysics::flyTo( const GeoDataLookAt &target, FlyToMode mode )
99{
100 d->m_timeline.stop();
101 d->m_source = d->m_presenter->lookAt();
102 d->m_target = target;
103 const ViewportParams *viewport = d->m_presenter->viewport();
104
105 FlyToMode effectiveMode = mode;
106 qreal x(0), y(0);
107 bool globeHidesPoint(false);
108 bool onScreen = viewport->screenCoordinates( target.coordinates(), x, y,
109 globeHidesPoint );
110 bool invisible = globeHidesPoint || !onScreen;
111
112 if (effectiveMode == Automatic)
113 {
114 bool zoom = qAbs(d->m_source.range()-target.range()) > 10;
115
116 if ( (invisible || zoom) ) {
117 effectiveMode = Jump;
118 } else {
119 effectiveMode = Linear;
120 }
121 }
122
123 d->m_mode = effectiveMode;
124
125 switch(effectiveMode)
126 {
127 case Instant:
128 d->m_presenter->flyTo( target, Instant );
129 return;
130 case Linear:
131 d->m_timeline.setDuration(300);
132 d->m_timeline.setEasingCurve(QEasingCurve::OutCurve);
133 break;
134 case Jump:
135 {
136 qreal duration = invisible ? 2000 : 1000;
137 d->m_timeline.setDuration(duration);
138 d->m_timeline.setEasingCurve(QEasingCurve::InOutSine);
139 }
140 break;
141 case Automatic:
142 Q_ASSERT(false);
143 break;
144 }
145
146 d->m_timeline.start();
147}
148
149void MarblePhysics::updateProgress(qreal progress)
150{
151 Q_ASSERT(d->m_mode != Instant);
152 Q_ASSERT(d->m_mode != Automatic);
153
154 if (progress >= 1.0)
155 {
156 d->m_presenter->flyTo( d->m_target, Instant );
157 d->m_presenter->setViewContext( Marble::Still );
158 return;
159 }
160
161 Q_ASSERT(progress >= 0.0 && progress < 1.0);
162 const GeoDataCoordinates interpolated = d->m_source.coordinates().interpolate(d->m_target.coordinates(), progress);
163 qreal range = d->suggestedRange(progress);
164
165 GeoDataLookAt intermediate;
166 intermediate.setCoordinates(interpolated);
167 intermediate.setRange(range);
168
169 d->m_presenter->setViewContext( Marble::Animation );
170 d->m_presenter->flyTo( intermediate, Instant );
171}
172
173void MarblePhysics::startStillMode()
174{
175 d->m_presenter->setViewContext( Marble::Still );
176}
177
178}
179
180#include "moc_MarblePhysics.cpp"
This file contains the headers for ViewportParams.
QAction * zoom(const QObject *recvr, const char *slot, QObject *parent)
Binds a QML item to a specific geodetic location in screen coordinates.
@ Automatic
A sane value is chosen automatically depending on animation settings and the action.
@ Instant
Change camera position immediately (no interpolation)
@ Linear
Linear interpolation of lon, lat and distance to ground.
@ Jump
Linear interpolation of lon and lat, distance increases towards the middle point, then decreases
@ Still
still image
@ Animation
animated view (e.g. while rotating the globe)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void setDuration(int duration)
void setEasingCurve(const QEasingCurve &curve)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:57:57 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.