Marble

MarblePhysics.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2008 Torsten Rahn <[email protected]>
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 
17 namespace Marble
18 {
19 
20 class MarblePhysicsPrivate {
21 public:
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 
83 MarblePhysics::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 
93 MarblePhysics::~MarblePhysics()
94 {
95  delete d;
96 }
97 
98 void 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 
149 void 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 
173 void MarblePhysics::startStillMode()
174 {
175  d->m_presenter->setViewContext( Marble::Still );
176 }
177 
178 }
179 
180 #include "moc_MarblePhysics.cpp"
@ Animation
animated view (e.g. while rotating the globe)
Definition: MarbleGlobal.h:68
@ Still
still image
Definition: MarbleGlobal.h:67
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
@ Jump
Linear interpolation of lon and lat, distance increases towards the middle point, then decreases
Definition: MarbleGlobal.h:166
QAction * zoom(const QObject *recvr, const char *slot, QObject *parent)
void setEasingCurve(const QEasingCurve &curve)
Binds a QML item to a specific geodetic location in screen coordinates.
FlyToMode
Describes possible flight mode (interpolation between source and target camera positions)
Definition: MarbleGlobal.h:162
@ Automatic
A sane value is chosen automatically depending on animation settings and the action.
Definition: MarbleGlobal.h:163
void setDuration(int duration)
@ Instant
Change camera position immediately (no interpolation)
Definition: MarbleGlobal.h:164
@ Linear
Linear interpolation of lon, lat and distance to ground.
Definition: MarbleGlobal.h:165
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Thu Sep 21 2023 04:12:27 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.