1 /*
2  * Stellarium
3  * Copyright (C) 2007 Fabien Chereau
4  * Copyright (C) 2015 Georg Zotti (offset view adaptations, Up vector fixes)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335, USA.
19  *
20  * TODO: Rewrite this class. It has been much of the easy feel of Stellarium, but is terribly complicated to maintain.
21  * Very likely some things can be  made clearer, more efficient, use modern Qt things instead of manual solutions, etc.
22  * Esp. the up-vector requirements for zenith views cause lots of headache.
23  * In case we want to have other mount modes (ecliptical, galactical, ...), several parts have to be extended.
24 */
25 
26 #include "StelMovementMgr.hpp"
27 #include "StelObjectMgr.hpp"
28 #include "StelModuleMgr.hpp"
29 #include "StelApp.hpp"
30 #include "StelCore.hpp"
31 #include "StelUtils.hpp"
32 #include "StelTranslator.hpp"
33 #include "StelPainter.hpp"
34 #include "StelProjector.hpp"
35 #include "LabelMgr.hpp"
36 #include "Planet.hpp"
37 #include "Orbit.hpp"
38 #include "StelActionMgr.hpp"
39 
40 #include <cmath>
41 #include <QString>
42 #include <QTextStream>
43 #include <QSettings>
44 #include <QKeyEvent>
45 #include <QDebug>
46 #include <QFont>
47 #include <QFontMetrics>
48 
getValue() const49 double Smoother::getValue() const
50 {
51 	double k = easingCurve.valueForProgress(progress / duration);
52 	return start * (1 - k) + aim * k;
53 }
54 
setTarget(double start,double aim,double duration)55 void Smoother::setTarget(double start, double aim, double duration)
56 {
57 	this->start = start;
58 	this->aim = aim;
59 	if (duration>0.001) // duration cannot be zero!
60 		this->duration = duration;
61 	else
62 		this->duration = 0.001;
63 	this->progress = 0.;
64 	// Compute best easing curve depending on the speed of animation.
65 	if (duration >= 1.0)
66 		easingCurve = QEasingCurve(QEasingCurve::InOutQuart);
67 	else
68 		easingCurve = QEasingCurve(QEasingCurve::OutQuad);
69 }
70 
update(double dt)71 void Smoother::update(double dt)
72 {
73 	progress = qMin(progress + dt, duration);
74 }
75 
finished() const76 bool Smoother::finished() const
77 {
78 	return progress >= duration;
79 }
80 
StelMovementMgr(StelCore * acore)81 StelMovementMgr::StelMovementMgr(StelCore* acore)
82 	: currentFov(60.)
83 	, initFov(60.)
84 	, minFov(0.001389)
85 	, maxFov(100.)
86 	, userMaxFov(360.)
87 	, deltaFov(0.0)
88 	, core(acore)
89 	, objectMgr(Q_NULLPTR)
90 	, flagLockEquPos(false)
91 	, flagTracking(false)
92 	, flagInhibitAllAutomoves(false)
93 	, isMouseMovingHoriz(false)
94 	, isMouseMovingVert(false)
95 	, flagEnableMoveAtScreenEdge(false)
96 	, flagEnableMouseNavigation(true)
97 	, flagEnableMouseZooming(true)
98 	, mouseZoomSpeed(30)
99 	, flagEnableZoomKeys(true)
100 	, flagEnableMoveKeys(true)
101 	, keyMoveSpeed(0.00025)
102 	, keyZoomSpeed(0.00025)
103 	, flagMoveSlow(false)
104 	, flagCustomPan(false)
105 	, rateX(0.0)
106 	, rateY(0.0)
107 	, movementsSpeedFactor(1.0)
108 	, move()
109 	, flagAutoMove(false)
110 	, zoomingMode(ZoomNone)
111 	, deltaAlt(0.0)
112 	, deltaAz(0.0)
113 	, flagManualZoom(false)
114 	, autoMoveDuration(1.5)
115 	, isDragging(false)
116 	, hasDragged(false)
117 	, previousX(0)
118 	, previousY(0)
119 	, beforeTimeDragTimeRate(0.0)
120 	, dragTimeMode(false)
121 	, zoomMove()
122 	, flagAutoZoom(false)
123 	, flagAutoZoomOutResetsDirection(false)
124 	, mountMode(MountAltAzimuthal)
125 	, initViewPos(1., 0., 0.)
126 	, initViewUp(0., 0., 1.)
127 	, viewDirectionJ2000(0., 1., 0.)
128 	, viewDirectionMountFrame(0., 1., 0.)
129 	, upVectorMountFrame(0.,0.,1.)
130 	, dragTriggerDistance(4.f)
131 	, viewportOffsetTimeline(Q_NULLPTR)
132 	, oldViewportOffset(0.0, 0.0)
133 	, targetViewportOffset(0.0, 0.0)
134 	, flagIndicationMountMode(false)
135 	, lastMessageID(0)
136 {
137 	setObjectName("StelMovementMgr");
138 }
139 
~StelMovementMgr()140 StelMovementMgr::~StelMovementMgr()
141 {
142 	if (viewportOffsetTimeline)
143 	{
144 		delete viewportOffsetTimeline;
145 		viewportOffsetTimeline=Q_NULLPTR;
146 	}
147 }
148 
init()149 void StelMovementMgr::init()
150 {
151 	conf = StelApp::getInstance().getSettings();
152 	objectMgr = GETSTELMODULE(StelObjectMgr);
153 	Q_ASSERT(conf);
154 	Q_ASSERT(objectMgr);
155 	connect(objectMgr, SIGNAL(selectedObjectChanged(StelModule::StelModuleSelectAction)),
156 		this, SLOT(selectedObjectChange(StelModule::StelModuleSelectAction)));
157 	connect(&StelApp::getInstance(), SIGNAL(languageChanged()), this, SLOT(bindingFOVActions()));
158 
159 	flagEnableMoveAtScreenEdge = conf->value("navigation/flag_enable_move_at_screen_edge",false).toBool();
160 	mouseZoomSpeed = conf->value("navigation/mouse_zoom",30).toInt();
161 	flagEnableZoomKeys = conf->value("navigation/flag_enable_zoom_keys", true).toBool();
162 	flagEnableMoveKeys = conf->value("navigation/flag_enable_move_keys", true).toBool();
163 	keyMoveSpeed = conf->value("navigation/move_speed",0.0004).toDouble();
164 	keyZoomSpeed = conf->value("navigation/zoom_speed", 0.0004).toDouble();
165 	autoMoveDuration = conf->value ("navigation/auto_move_duration",1.5f).toFloat();
166 	flagManualZoom = conf->value("navigation/flag_manual_zoom").toBool();
167 	flagAutoZoomOutResetsDirection = conf->value("navigation/auto_zoom_out_resets_direction", true).toBool();
168 	flagEnableMouseNavigation = conf->value("navigation/flag_enable_mouse_navigation",true).toBool();
169 	flagEnableMouseZooming = conf->value("navigation/flag_enable_mouse_zooming",true).toBool();
170 	flagIndicationMountMode = conf->value("gui/flag_indication_mount_mode", false).toBool();
171 
172 	minFov = conf->value("navigation/min_fov",0.001389).toDouble(); // default: minimal FOV = 5"
173 	userMaxFov = conf->value("navigation/max_fov",360.).toDouble(); // default: 360°=no real limit. maxFov then depends on projection only.
174 	initFov = conf->value("navigation/init_fov",60.0).toDouble();
175 	currentFov = initFov;
176 
177 	// we must set mount mode before potentially loading zenith views etc.
178 	QString tmpstr = conf->value("navigation/viewing_mode", "horizon").toString();
179 	if (tmpstr.contains("equator", Qt::CaseInsensitive))
180 		setMountMode(StelMovementMgr::MountEquinoxEquatorial);
181 	else
182 	{
183 		if (tmpstr.contains("horizon", Qt::CaseInsensitive))
184 			setMountMode(StelMovementMgr::MountAltAzimuthal);
185 		else
186 		{
187 			qWarning() << "ERROR: Unknown viewing mode type: " << tmpstr;
188 			setMountMode(StelMovementMgr::MountEquinoxEquatorial);
189 		}
190 	}
191 
192 	// With a special code of init_view_position=x/y/1 (or actually, anything equal or larger to 1) you can set zenith into the center and atan2(x/y) to bottom of screen.
193 	// examples:  1/0   ->0     NORTH is bottom
194 	//           -1/0   ->180   SOUTH is bottom
195 	//            0/-1  -> 90   EAST is bottom
196 	//            0/1   ->270   WEST is bottom
197 	Vec3f tmp(conf->value("navigation/init_view_pos", "1,0,0").toString());
198 	//qDebug() << "initViewPos" << tmp[0] << "/" << tmp[1] << "/" << tmp[2];
199 	if (tmp[2]>=1)
200 	{
201 		//qDebug() << "Special zenith setup:";
202 		setViewDirectionJ2000(mountFrameToJ2000(Vec3d(0., 0., 1.)));
203 		initViewPos.set(0., 0., 1.);
204 
205 		// It is not good to code 0/0/1 as view vector: bottom azimuth is undefined. Use default-south:
206 		if ((tmp[0]==0.f) && (tmp[1]==0.f))
207 			tmp[0]=-1.;
208 
209 		upVectorMountFrame.set(static_cast<double>(tmp[0]), static_cast<double>(tmp[1]), 0.);
210 		upVectorMountFrame.normalize();
211 		initViewUp=upVectorMountFrame;
212 		//qDebug() << "InitViewUp: " << initViewUp;
213 	}
214 	else
215 	{
216 		//qDebug() << "simpler view vectors...";
217 		//qDebug() << "   initViewPos becomes " << tmp[0] << "/" << tmp[1] << "/" << tmp[2];
218 		initViewPos = tmp.toVec3d();
219 		//qDebug() << "   initViewPos is " << initViewPos[0] << "/" << initViewPos[1] << "/" << initViewPos[2];
220 		viewDirectionJ2000 = core->altAzToJ2000(initViewPos, StelCore::RefractionOff);
221 		//qDebug() << "viewDirectionJ2000: " << viewDirectionJ2000[0] << "/" << viewDirectionJ2000[1] << "/" << viewDirectionJ2000[2];
222 		setViewDirectionJ2000(viewDirectionJ2000);
223 		//qDebug() << "   up2000 initViewUp becomes " << initViewUp[0] << "/" << initViewUp[1] << "/" << initViewUp[2];
224 		setViewUpVector(initViewUp);
225 
226 		//qDebug() << "   upVectorMountFrame becomes " << upVectorMountFrame[0] << "/" << upVectorMountFrame[1] << "/" << upVectorMountFrame[2];
227 	}
228 
229 
230 	QString movementGroup = N_("Movement and Selection");
231 	addAction("actionSwitch_Equatorial_Mount", N_("Miscellaneous"), N_("Switch between equatorial and azimuthal mount"), "equatorialMount", "Ctrl+M");
232 	addAction("actionGoto_Selected_Object", movementGroup, N_("Center on selected object"), "tracking", "Space");
233 	addAction("actionGoto_Deselection", movementGroup, N_("Deselect the selected object"), "deselection()", "Ctrl+Space");
234 	addAction("actionZoom_In_Auto", movementGroup, N_("Zoom in on selected object"), "autoZoomIn()", "/");
235 	addAction("actionZoom_Out_Auto", movementGroup, N_("Zoom out"), "autoZoomOut()", "\\");
236 	// AW: Same behaviour has action "actionGoto_Selected_Object" by the fact (Is it for backward compatibility?)
237 	addAction("actionSet_Tracking", movementGroup, N_("Track object"), "tracking", "T");
238 	// Implementation of quick turning to different directions (examples: CdC, HNSKY)
239 	addAction("actionLook_Towards_East", movementGroup, N_("Look towards East"), "lookEast()", "Shift+E");
240 	addAction("actionLook_Towards_West", movementGroup, N_("Look towards West"), "lookWest()", "Shift+W");
241 	addAction("actionLook_Towards_North", movementGroup, N_("Look towards North"), "lookNorth()", "Shift+N");
242 	addAction("actionLook_Towards_South", movementGroup, N_("Look towards South"), "lookSouth()", "Shift+S");
243 	addAction("actionLook_Towards_Zenith", movementGroup, N_("Look towards Zenith"), "lookZenith()", "Shift+Z");
244 	// Additional hooks
245 	addAction("actionLook_Towards_NCP", movementGroup, N_("Look towards North Celestial pole"), "lookTowardsNCP()", "Alt+Shift+N");
246 	addAction("actionLook_Towards_SCP", movementGroup, N_("Look towards South Celestial pole"), "lookTowardsSCP()", "Alt+Shift+S");
247 	// Field of view
248 	// The feature was moved from FOV plugin
249 	bindingFOVActions();
250 
251 	viewportOffsetTimeline=new QTimeLine(1000, this);
252 	viewportOffsetTimeline->setFrameRange(0, 100);
253 	connect(viewportOffsetTimeline, SIGNAL(valueChanged(qreal)), this, SLOT(handleViewportOffsetMovement(qreal)));
254 	targetViewportOffset.set(core->getViewportHorizontalOffset(), core->getViewportVerticalOffset());
255 }
256 
bindingFOVActions()257 void StelMovementMgr::bindingFOVActions()
258 {
259 	StelActionMgr* actionMgr = StelApp::getInstance().getStelActionManager();
260 	QString confval, tfov, fovGroup = N_("Field of View"), fovText = q_("Set predefined FOV");
261 	QList<float> defaultFOV = { 0.5f, 180.f, 90.f, 60.f, 45.f, 20.f, 10.f, 5.f, 2.f, 1.f };
262 	for (int i = 0; i < defaultFOV.size(); ++i)
263 	{
264 		confval = QString("fov/quick_fov_%1").arg(i);
265 		const double cfov = conf->value(confval, defaultFOV.at(i)).toDouble();
266 		tfov = QString::number(cfov, 'f', 2);
267 		QString actionName = QString("actionSet_FOV_%1").arg(i);
268 		QString actionDescription = QString("%1 #%2 (%3%4)").arg(fovText, QString::number(i), tfov, QChar(0x00B0));
269 		StelAction* action = actionMgr->findAction(actionName);
270 		if (action!=Q_NULLPTR)
271 			actionMgr->findAction(actionName)->setText(actionDescription);
272 		else
273 			addAction(actionName, fovGroup, actionDescription, this, [=](){setFOVDeg(static_cast<float>(cfov));}, QString("Ctrl+Alt+%1").arg(i));
274 	}
275 }
276 
setEquatorialMount(bool b)277 void StelMovementMgr::setEquatorialMount(bool b)
278 {
279 	setMountMode(b ? MountEquinoxEquatorial : MountAltAzimuthal);
280 
281 	if (getFlagIndicationMountMode())
282 	{
283 		QString mode = qc_("Equatorial mount", "mount mode");
284 		if (!b)
285 			mode = qc_("Alt-azimuth mount", "mount mode");
286 
287 		if (lastMessageID)
288 			GETSTELMODULE(LabelMgr)->deleteLabel(lastMessageID);
289 
290 		StelProjector::StelProjectorParams projectorParams = StelApp::getInstance().getCore()->getCurrentStelProjectorParams();
291 		StelPainter painter(StelApp::getInstance().getCore()->getProjection2d());
292 		int yPositionOffset = qRound(projectorParams.viewportXywh[3]*projectorParams.viewportCenterOffset[1]);
293 		int xPosition = qRound(projectorParams.viewportCenter[0] - painter.getFontMetrics().boundingRect(mode).width()/2);
294 		int yPosition = qRound(projectorParams.viewportCenter[1] - yPositionOffset - painter.getFontMetrics().height()/2);
295 		lastMessageID = GETSTELMODULE(LabelMgr)->labelScreen(mode, xPosition, yPosition, true, StelApp::getInstance().getScreenFontSize() + 3, "#99FF99", true, 2000);
296 	}
297 }
298 
setMountMode(MountMode m)299 void StelMovementMgr::setMountMode(MountMode m)
300 {
301 	mountMode = m;
302 	setViewDirectionJ2000(viewDirectionJ2000);
303 	// TODO: Decide whether re-setting Up-vector is required here.
304 	//setViewUpVector(Vec3d(0., 0., 1.));
305 	//setViewUpVectorJ2000(Vec3d(0., 0., 1.)); // Looks wrong on start.
306 	emit equatorialMountChanged(m==MountEquinoxEquatorial);
307 }
308 
setFlagLockEquPos(bool b)309 void StelMovementMgr::setFlagLockEquPos(bool b)
310 {
311 	flagLockEquPos=b;
312 }
313 
setViewUpVectorJ2000(const Vec3d & up)314 void StelMovementMgr::setViewUpVectorJ2000(const Vec3d& up)
315 {
316 	//qDebug() << "setViewUpvectorJ2000()";
317 	upVectorMountFrame = j2000ToMountFrame(up);
318 }
319 
320 // For simplicity you can set this directly. Take care when looking into poles like zenith in altaz mode:
321 // We have a problem if alt=+/-90degrees: view and up angles are ill-defined (actually, angle between them=0 and therefore we saw shaky rounding effects), therefore Bug LP:1068529
setViewUpVector(const Vec3d & up)322 void StelMovementMgr::setViewUpVector(const Vec3d& up)
323 {
324 	//qDebug() << "setViewUpvector()";
325 	upVectorMountFrame = up;
326 }
327 
getViewUpVectorJ2000() const328 Vec3d StelMovementMgr::getViewUpVectorJ2000() const
329 {
330 	return mountFrameToJ2000(upVectorMountFrame);
331 }
332 
handleMouseMoves(int x,int y,Qt::MouseButtons)333 bool StelMovementMgr::handleMouseMoves(int x, int y, Qt::MouseButtons)
334 {
335 	// Turn if the mouse is at the edge of the screen unless config asks otherwise
336 	if (flagEnableMoveAtScreenEdge)
337 	{
338 		if (x <= 1)
339 		{
340 			turnLeft(true);
341 			isMouseMovingHoriz = true;
342 		}
343 		else if (x >= core->getProjection2d()->getViewportWidth() - 2)
344 		{
345 			turnRight(true);
346 			isMouseMovingHoriz = true;
347 		}
348 		else if (isMouseMovingHoriz)
349 		{
350 			turnLeft(false);
351 			isMouseMovingHoriz = false;
352 		}
353 
354 		if (y <= 1)
355 		{
356 			turnUp(true);
357 			isMouseMovingVert = true;
358 		}
359 		else if (y >= core->getProjection2d()->getViewportHeight() - 2)
360 		{
361 			turnDown(true);
362 			isMouseMovingVert = true;
363 		}
364 		else if (isMouseMovingVert)
365 		{
366 			turnUp(false);
367 			isMouseMovingVert = false;
368 		}
369 	}
370 
371 
372 	if (isDragging && flagEnableMouseNavigation)
373 	{
374 		if (hasDragged || (sqrtf(static_cast<float>((x-previousX)*(x-previousX) +(y-previousY)*(y-previousY)))>dragTriggerDistance))
375 		{
376 			hasDragged = true;
377 			setFlagTracking(false);
378 			dragView(previousX, previousY, x, y);
379 			previousX = x;
380 			previousY = y;
381 			// We can hardly use the mouse exactly enough to go to the zenith/pole. Any mouse motion can safely reset the simplified up vector.
382 			//qDebug() << "handleMouseMoves: resetting Up vector.";
383 			setViewUpVector(Vec3d(0., 0., 1.));
384 			return true;
385 		}
386 	}
387 	return false;
388 }
389 
getCallOrder(StelModuleActionName actionName) const390 double StelMovementMgr::getCallOrder(StelModuleActionName actionName) const
391 {
392 	// allow plugins to intercept keys by using a lower number than this!
393 	if (actionName == StelModule::ActionHandleKeys)
394 		return 5;
395 	return 0;
396 }
397 
398 
399 
handleKeys(QKeyEvent * event)400 void StelMovementMgr::handleKeys(QKeyEvent* event)
401 {
402 	GimbalOrbit *gimbal=Q_NULLPTR;
403 #ifdef USE_GIMBAL_ORBIT
404 	StelCore *core=StelApp::getInstance().getCore();
405 	Planet* obsPlanet= core->getCurrentPlanet().data();
406 	if (obsPlanet->getPlanetType()==Planet::isObserver)
407 	{
408 		gimbal=static_cast<GimbalOrbit*>(obsPlanet->getOrbit());
409 	}
410 #endif
411         if (event->type() == QEvent::KeyPress)
412 	{
413 		// qDebug() << "Modifiers:" << event->modifiers();
414 		// FIXME: Alt modifier seems problematic. The keys to modify distance in an observer gimbal seem to
415 		// collide with operating system hotkeys. Using Alt5/Alt6 is experimental just to have "some" working solution.
416 		// Direction and zoom deplacements
417 		switch (event->key())
418 		{
419 			case Qt::Key_Left:
420 				if (gimbal && event->modifiers().testFlag(Qt::AltModifier)){
421 					gimbal->addToLongitude(-5.);
422 				} else {
423 					turnLeft(true);
424 				}
425 				break;
426 			case Qt::Key_Right:
427 				if (gimbal && event->modifiers().testFlag(Qt::AltModifier)){
428 					gimbal->addToLongitude(5.);
429 				} else
430 				turnRight(true);
431 				break;
432 			case Qt::Key_Up:
433 				if (gimbal && event->modifiers().testFlag(Qt::AltModifier)){
434 					gimbal->addToLatitude(5.);
435 				}
436 				else if (event->modifiers().testFlag(Qt::ControlModifier)){
437 					zoomIn(true);
438 				} else {
439 					turnUp(true);
440 				}
441 				break;
442 			case Qt::Key_Down:
443 				if (gimbal && event->modifiers().testFlag(Qt::AltModifier)){
444 					gimbal->addToLatitude(-5.);
445 				}
446 				else if (event->modifiers().testFlag(Qt::ControlModifier)) {
447 					zoomOut(true);
448 				} else {
449 					turnDown(true);
450 				}
451 				break;
452 			case Qt::Key_PageUp:
453 			case Qt::Key_multiply:
454 				zoomIn(true);
455 				break;
456 			case Qt::Key_PageDown:
457 			case Qt::Key_division:
458 				zoomOut(true);
459 				break;
460 			case Qt::Key_End:
461 				if (gimbal && event->modifiers().testFlag(Qt::AltModifier))
462 				{
463 					gimbal->addToDistance(gimbal->getDistance()*0.05);
464 				}
465 				break;
466 			case Qt::Key_Home:
467 				if (gimbal && event->modifiers().testFlag(Qt::AltModifier))
468 				{
469 					gimbal->addToDistance(-gimbal->getDistance()*0.05);
470 				}
471 				break;
472 			case Qt::Key_Shift:
473 				moveSlow(true); break;
474 			default:
475 				return;
476 		}
477 	}
478 	else
479 	{
480 		// When a deplacement key is released stop moving
481 		switch (event->key())
482 		{
483 			case Qt::Key_Left:
484 				turnLeft(false); break;
485 			case Qt::Key_Right:
486 				turnRight(false); break;
487 			case Qt::Key_Up:
488 				zoomIn(false);
489 				turnUp(false);
490 				break;
491 			case Qt::Key_Down:
492 				zoomOut(false);
493 				turnDown(false);
494 				break;
495 			case Qt::Key_PageUp:
496 				zoomIn(false); break;
497 			case Qt::Key_PageDown:
498 				zoomOut(false); break;
499 			case Qt::Key_Shift:
500 				moveSlow(false); break;
501 			case Qt::Key_Control:
502 				// This can be all that is seen for anything with control, so stop them all.
503 				// This is true for 4.8.1
504 				turnRight(false);
505 				turnLeft(false);
506 				zoomIn(false);
507 				zoomOut(false);
508 				turnDown(false);
509 				turnUp(false);
510 				dragTimeMode=false;
511 				break;
512 			default:
513 				return;
514 		}
515 	}
516 	event->accept();
517 }
518 
519 //! Handle mouse wheel events.
handleMouseWheel(QWheelEvent * event)520 void StelMovementMgr::handleMouseWheel(QWheelEvent* event)
521 {
522 	if (flagEnableMouseZooming==false)
523 		return;
524 
525 	// This managed only vertical wheel events.
526 	// However, Alt-wheel switches this to horizontal, so allow alt-wheel and handle angles properly!
527 	const double numSteps = (event->angleDelta().x() + event->angleDelta().y()) / 120.;
528 
529 	if (event->modifiers() & Qt::ControlModifier)
530 	{
531 		if ((event->modifiers() & Qt::AltModifier) && (event->modifiers() & Qt::ShiftModifier))
532 		{
533 			// move time by years
534 			double jdNow=core->getJD();
535 			int year, month, day, hour, min, sec, millis;
536 			StelUtils::getDateFromJulianDay(jdNow, &year, &month, &day);
537 			StelUtils::getTimeFromJulianDay(jdNow, &hour, &min, &sec, &millis);
538 			double jdNew;
539 			StelUtils::getJDFromDate(&jdNew, year+qRound(numSteps), month, day, hour, min, sec);
540 			core->setJD(jdNew);
541 			emit core->dateChanged();
542 			emit core->dateChangedByYear();
543 		}
544 		else if (event->modifiers() & Qt::AltModifier)
545 		{
546 			// move time by days
547 			core->setJD(core->getJD()+qRound(numSteps));
548 			emit core->dateChanged();
549 		}
550 		else if (event->modifiers() & Qt::ShiftModifier)
551 		{
552 			// move time by hours
553 			core->setJD(core->getJD()+qRound(numSteps)/(24.));
554 		}
555 		else
556 		{
557 			// move time by minutes
558 			core->setJD(core->getJD()+qRound(numSteps)/(24.*60.));
559 		}
560 	}
561 	else
562 	{
563 		const double zoomFactor = exp(-mouseZoomSpeed * numSteps / 60.);
564 		const float zoomDuration = 0.2f;
565 		zoomTo(getAimFov() * zoomFactor, zoomDuration);
566 	}
567 	event->accept();
568 }
569 
addTimeDragPoint(int x,int y)570 void StelMovementMgr::addTimeDragPoint(int x, int y)
571 {
572 	DragHistoryEntry e;
573 	e.runTime=StelApp::getInstance().getTotalRunTime();
574 	e.jd=core->getJD();
575 	e.x=x;
576 	e.y=y;
577 	timeDragHistory.append(e);
578 	if (timeDragHistory.size()>3)
579 		timeDragHistory.removeFirst();
580 }
581 
handlePinch(qreal scale,bool started)582 bool StelMovementMgr::handlePinch(qreal scale, bool started)
583 {
584 #ifdef Q_OS_WIN
585 	if (flagEnableMouseNavigation == false || flagEnableMouseZooming==false)
586 		return true;
587 #endif
588 
589 	static double previousFov = 0;
590 	if (started)
591 		previousFov = getAimFov();
592 	if (scale>0)
593 		zoomTo(previousFov/scale, 0);
594 	return true;
595 }
596 
handleMouseClicks(QMouseEvent * event)597 void StelMovementMgr::handleMouseClicks(QMouseEvent* event)
598 {
599 	switch (event->button())
600 	{
601 		case Qt::RightButton:
602 		{
603 			if (event->type()==QEvent::MouseButtonRelease)
604 			{
605 				// The code for deselect the selected object was moved into separate method.
606 				deselection();
607 				event->accept();
608 				return;
609 			}
610 			break;
611 		}
612 		case Qt::LeftButton :
613 			if (event->type()==QEvent::MouseButtonDblClick)
614 			{
615 				if (objectMgr->getWasSelected())
616 				{
617 					moveToObject(objectMgr->getSelectedObject()[0],autoMoveDuration);
618 					setFlagTracking(true);
619 				}
620 				event->accept();
621 				return;
622 			}
623 			else if (event->type()==QEvent::MouseButtonPress)
624 			{
625 				if (event->modifiers() & Qt::ControlModifier)
626 				{
627 					dragTimeMode=true;
628 					beforeTimeDragTimeRate=core->getTimeRate();
629 					timeDragHistory.clear();
630 					addTimeDragPoint(event->x(), event->y());
631 				}
632 				isDragging = true;
633 				hasDragged = false;
634 				previousX = event->x();
635 				previousY = event->y();
636 				event->accept();
637 				return;
638 			}
639 			else if (event->type()==QEvent::MouseButtonRelease)
640 			{
641 				isDragging = false;
642 				if (hasDragged)
643 				{
644 					event->accept();
645 					if (dragTimeMode)
646 					{
647 						if (timeDragHistory.size()>=3)
648 						{
649 							const double deltaT = timeDragHistory.last().runTime-timeDragHistory.first().runTime;
650 							Vec2f d(timeDragHistory.last().x-timeDragHistory.first().x, timeDragHistory.last().y-timeDragHistory.first().y);
651 							if (d.length()/static_cast<float>(deltaT) < dragTriggerDistance)
652 							{
653 								core->setTimeRate(StelCore::JD_SECOND);
654 							}
655 							else
656 							{
657 								const double deltaJd = timeDragHistory.last().jd-timeDragHistory.first().jd;
658 								const double newTimeRate = deltaJd/deltaT;
659 								if (deltaT>0.00000001)
660 								{
661 									if (newTimeRate>=0)
662 										core->setTimeRate(qMax(newTimeRate, StelCore::JD_SECOND));
663 									else
664 										core->setTimeRate(qMin(newTimeRate, -StelCore::JD_SECOND));
665 								}
666 								else
667 									core->setTimeRate(beforeTimeDragTimeRate);
668 							}
669 						}
670 						else
671 							core->setTimeRate(beforeTimeDragTimeRate);
672 					}
673 					return;
674 				}
675 				else // has not dragged...
676 				{
677 					// It's a normal click release
678 					// TODO: Leave time dragging in Natural speed or zero speed (config option?) if mouse was resting
679 			#ifdef Q_OS_MAC
680 					// CTRL + left click = right click for 1 button mouse
681 					if (event->modifiers().testFlag(Qt::ControlModifier))
682 					{
683 						objectMgr->unSelect();
684 						event->accept();
685 						return;
686 					}
687 
688 					// Try to select object at that position
689 					objectMgr->findAndSelect(core, event->x(), event->y(), event->modifiers().testFlag(Qt::MetaModifier) ? StelModule::AddToSelection : StelModule::ReplaceSelection);
690 			#else
691 					objectMgr->findAndSelect(core, event->x(), event->y(), event->modifiers().testFlag(Qt::ControlModifier) ? StelModule::AddToSelection : StelModule::ReplaceSelection);
692 			#endif
693 					if (objectMgr->getWasSelected())
694 						setFlagTracking(false);
695 					//GZ: You must comment out this line for testing Landscape transparency debug prints.
696 					//event->accept();
697 					return;
698 				}
699 			}
700 			else
701 			{
702 				qDebug() << "StelMovementMgr::handleMouseClicks: unknown mouse event type, skipping: " << event->type();
703 			}
704 			break;
705 		case Qt::MidButton :
706 			if (event->type()==QEvent::MouseButtonRelease)
707 			{
708 				if (objectMgr->getWasSelected())
709 				{
710 					moveToObject(objectMgr->getSelectedObject()[0],autoMoveDuration);
711 					setFlagTracking(true);
712 				}
713 			}
714 			break;
715 		default: break;
716 	}
717 	return;
718 }
719 
setInitFov(double fov)720 void StelMovementMgr::setInitFov(double fov)
721 {
722 	initFov=fov;
723 	StelApp::getInstance().getSettings()->setValue("navigation/init_fov", fov);
724 }
725 
setInitViewDirectionToCurrent()726 void StelMovementMgr::setInitViewDirectionToCurrent()
727 {
728 	// 2016-12 TODO: Create azimuth indication for zenith views.
729 	initViewPos = core->j2000ToAltAz(viewDirectionJ2000, StelCore::RefractionOff);
730 	QString dirStr = QString("%1,%2,%3").arg(initViewPos[0]).arg(initViewPos[1]).arg(initViewPos[2]);
731 	StelApp::getInstance().getSettings()->setValue("navigation/init_view_pos", dirStr);
732 }
733 
734 /*************************************************************************
735  The selected objects changed, follow it if we were already following another one
736 *************************************************************************/
selectedObjectChange(StelModule::StelModuleSelectAction)737 void StelMovementMgr::selectedObjectChange(StelModule::StelModuleSelectAction)
738 {
739 	// If an object was selected keep the earth following
740 	if (objectMgr->getWasSelected())
741 	{
742 		if (getFlagTracking())
743 			setFlagLockEquPos(true);
744 		setFlagTracking(false);
745 	}
746 }
747 
turnRight(bool s)748 void StelMovementMgr::turnRight(bool s)
749 {
750 	if (s && flagEnableMoveKeys)
751 	{
752 		deltaAz = 1;
753 		setFlagTracking(false);
754 		setFlagLockEquPos(false);
755 	}
756 	else
757 		deltaAz = 0;
758 }
759 
turnLeft(bool s)760 void StelMovementMgr::turnLeft(bool s)
761 {
762 	if (s && flagEnableMoveKeys)
763 	{
764 		deltaAz = -1;
765 		setFlagTracking(false);
766 		setFlagLockEquPos(false);
767 	}
768 	else
769 		deltaAz = 0;
770 }
771 
turnUp(bool s)772 void StelMovementMgr::turnUp(bool s)
773 {
774 	if (s && flagEnableMoveKeys)
775 	{
776 		deltaAlt = 1;
777 		setFlagTracking(false);
778 		setFlagLockEquPos(false);
779 	}
780 	else
781 		deltaAlt = 0;
782 }
783 
turnDown(bool s)784 void StelMovementMgr::turnDown(bool s)
785 {
786 	if (s && flagEnableMoveKeys)
787 	{
788 		deltaAlt = -1;
789 		setFlagTracking(false);
790 		setFlagLockEquPos(false);
791 	}
792 	else
793 		deltaAlt = 0;
794 }
795 
796 
zoomIn(bool s)797 void StelMovementMgr::zoomIn(bool s)
798 {
799 	if (flagEnableZoomKeys)
800 		deltaFov = -1*(s!=0);
801 }
802 
zoomOut(bool s)803 void StelMovementMgr::zoomOut(bool s)
804 {
805 	if (flagEnableZoomKeys)
806 		deltaFov = (s!=0);
807 }
808 
lookEast(bool zero)809 void StelMovementMgr::lookEast(bool zero)
810 {
811 	float alt, cy;
812 	Vec3f dir;
813 
814 	if (zero)
815 	{
816 		alt = 0.0f;
817 		cy = M_PI_2f;
818 		StelUtils::spheToRect(cy, alt, dir);
819 
820 		setViewDirectionJ2000(core->altAzToJ2000(dir.toVec3d(), StelCore::RefractionOff));
821 		setViewUpVector(Vec3d(0., 0., 1.));
822 		return;
823 	}
824 
825 	StelUtils::rectToSphe(&cy,&alt,core->j2000ToAltAz(getViewDirectionJ2000(), StelCore::RefractionOff));
826 	cy = M_PI_2f;
827 	StelUtils::spheToRect(cy, alt, dir);
828 	setViewDirectionJ2000(core->altAzToJ2000(dir.toVec3d(), StelCore::RefractionOff));
829 	//qDebug() << "Setting East at Alt:" << alt*M_180_PIf;
830 	if ((mountMode==MountAltAzimuthal) && (fabsf(alt)>M_PI_2f-0.0001f) && (fabs(upVectorMountFrame[2])<0.001))
831 	{
832 		// Special case: we already look into zenith (with rounding tolerance). Bring East to bottom of screen.
833 		upVectorMountFrame.set(0., -1.*StelUtils::sign(alt), 0.);
834 		//qDebug() << "lookEast: better Up is " << upVectorMountFrame[0] << "/" << upVectorMountFrame[1] << "/" << upVectorMountFrame[2];
835 	}
836 }
837 
lookWest(bool zero)838 void StelMovementMgr::lookWest(bool zero)
839 {
840 	float alt, cy;
841 	Vec3f dir;
842 
843 	if (zero)
844 	{
845 		alt = 0.0f;
846 		cy = 3.f*M_PI_2f;
847 		StelUtils::spheToRect(cy, alt, dir);
848 
849 		setViewDirectionJ2000(core->altAzToJ2000(dir.toVec3d(), StelCore::RefractionOff));
850 		setViewUpVector(Vec3d(0., 0., 1.));
851 		return;
852 	}
853 
854 	StelUtils::rectToSphe(&cy,&alt,core->j2000ToAltAz(getViewDirectionJ2000(), StelCore::RefractionOff));
855 	cy = 3.f*M_PI_2f;
856 	StelUtils::spheToRect(cy, alt, dir);
857 	setViewDirectionJ2000(core->altAzToJ2000(dir.toVec3d(), StelCore::RefractionOff));
858 	//qDebug() << "Setting West at Alt:" << alt*M_180_PIf;
859 	if ((mountMode==MountAltAzimuthal) &&  (fabsf(alt)>M_PI_2f-0.0001f) && (fabs(upVectorMountFrame[2])<0.001))
860 	{
861 		// Special case: we already look into zenith (with rounding tolerance). Bring West to bottom of screen.
862 		upVectorMountFrame.set(0., StelUtils::sign(alt), 0.);
863 		//qDebug() << "lookEast: better Up is " << upVectorMountFrame[0] << "/" << upVectorMountFrame[1] << "/" << upVectorMountFrame[2];
864 	}
865 }
866 
lookNorth(bool zero)867 void StelMovementMgr::lookNorth(bool zero)
868 {
869 	float alt, cy;
870 	Vec3f dir;
871 
872 	if (zero)
873 	{
874 		alt = 0.0f;
875 		cy = M_PIf;
876 		StelUtils::spheToRect(cy, alt, dir);
877 
878 		setViewDirectionJ2000(core->altAzToJ2000(dir.toVec3d(), StelCore::RefractionOff));
879 		setViewUpVector(Vec3d(0., 0., 1.));
880 		return;
881 	}
882 
883 	StelUtils::rectToSphe(&cy,&alt,core->j2000ToAltAz(getViewDirectionJ2000(), StelCore::RefractionOff));
884 	cy = static_cast<float>(M_PI);
885 	StelUtils::spheToRect(cy, alt, dir);
886 	setViewDirectionJ2000(core->altAzToJ2000(dir.toVec3d(), StelCore::RefractionOff));
887 
888 	//qDebug() << "Setting North at Alt:" << alt*M_180_PIf;
889 	if ((mountMode==MountAltAzimuthal) &&  (fabsf(alt)>M_PI_2f-0.0001f) && (fabs(upVectorMountFrame[2])<0.001))
890 	{
891 		// Special case: we already look into zenith (with rounding tolerance). Bring North to bottom of screen.
892 		upVectorMountFrame.set(StelUtils::sign(alt), 0., 0.);
893 		//qDebug() << "lookNorth: better Up is " << upVectorMountFrame[0] << "/" << upVectorMountFrame[1] << "/" << upVectorMountFrame[2];
894 	}
895 }
896 
lookSouth(bool zero)897 void StelMovementMgr::lookSouth(bool zero)
898 {
899 	float alt, cy;
900 	Vec3f dir;
901 
902 	if (zero)
903 	{
904 		alt = 0.0f;
905 		cy = 0.0f;
906 		StelUtils::spheToRect(cy, alt, dir);
907 
908 		setViewDirectionJ2000(core->altAzToJ2000(dir.toVec3d(), StelCore::RefractionOff));
909 		setViewUpVector(Vec3d(0., 0., 1.));
910 		return;
911 	}
912 
913 	StelUtils::rectToSphe(&cy,&alt,core->j2000ToAltAz(getViewDirectionJ2000(), StelCore::RefractionOff));
914 	cy = 0.f;
915 	StelUtils::spheToRect(cy, alt, dir);
916 	setViewDirectionJ2000(core->altAzToJ2000(dir.toVec3d(), StelCore::RefractionOff));
917 
918 	//qDebug() << "Setting South at Alt:" << alt*M_180_PIf;
919 	if ((mountMode==MountAltAzimuthal) &&  (fabsf(alt)>M_PI_2f-0.0001f) && (fabs(upVectorMountFrame[2])<0.001))
920 	{
921 		// Special case: we already look into zenith (with rounding tolerance). Bring South to bottom of screen.
922 		upVectorMountFrame.set(-1.*StelUtils::sign(alt), 0., 0.);
923 		//qDebug() << "lookSouth: better Up is " << upVectorMountFrame[0] << "/" << upVectorMountFrame[1] << "/" << upVectorMountFrame[2];
924 	}
925 }
926 
lookZenith(void)927 void StelMovementMgr::lookZenith(void)
928 {
929 	Vec3f dir;
930 	StelUtils::spheToRect(M_PIf, M_PI_2f, dir);
931 	//qDebug() << "lookZenith: Up is " << upVectorMountFrame[0] << "/" << upVectorMountFrame[1] << "/" << upVectorMountFrame[2];
932 	setViewDirectionJ2000(core->altAzToJ2000(dir.toVec3d(), StelCore::RefractionOff));
933 	//qDebug() << "lookZenith: View is " << viewDirectionMountFrame[0] << "/" << viewDirectionMountFrame[1] << "/" << viewDirectionMountFrame[2];
934 	if (mountMode==MountAltAzimuthal)
935 	{	// ensure a stable up vector that makes the bottom of the screen point south.
936 		upVectorMountFrame.set(-1., 0., 0.);
937 		//qDebug() << "lookZenith: better Up is " << upVectorMountFrame[0] << "/" << upVectorMountFrame[1] << "/" << upVectorMountFrame[2];
938 	}
939 }
940 
lookNadir(void)941 void StelMovementMgr::lookNadir(void)
942 {
943 	Vec3f dir;
944 	StelUtils::spheToRect(M_PIf, -M_PI_2f, dir);
945 	//qDebug() << "lookNadir: Up is " << upVectorMountFrame[0] << "/" << upVectorMountFrame[1] << "/" << upVectorMountFrame[2];
946 	setViewDirectionJ2000(core->altAzToJ2000(dir.toVec3d(), StelCore::RefractionOff));
947 	//qDebug() << "lookNadir: View is " << viewDirectionMountFrame[0] << "/" << viewDirectionMountFrame[1] << "/" << viewDirectionMountFrame[2];
948 	if (mountMode==MountAltAzimuthal)
949 	{	// ensure a stable up vector that makes the top of the screen point south.
950 		upVectorMountFrame.set(-1., 0., 0.);
951 		//qDebug() << "lookNadir: better Up is " << upVectorMountFrame[0] << "/" << upVectorMountFrame[1] << "/" << upVectorMountFrame[2];
952 	}
953 }
954 
lookTowardsNCP(void)955 void StelMovementMgr::lookTowardsNCP(void)
956 {
957 	setViewDirectionJ2000(core->equinoxEquToJ2000(Vec3d(0,0,1), StelCore::RefractionOff));
958 }
959 
lookTowardsSCP(void)960 void StelMovementMgr::lookTowardsSCP(void)
961 {
962 	setViewDirectionJ2000(core->equinoxEquToJ2000(Vec3d(0,0,-1), StelCore::RefractionOff));
963 }
964 
setFOVDeg(float fov)965 void StelMovementMgr::setFOVDeg(float fov)
966 {
967 	zoomTo(fov, 1.f);
968 }
969 
970 // Increment/decrement smoothly the vision field and position
updateMotion(double deltaTime)971 void StelMovementMgr::updateMotion(double deltaTime)
972 {
973 	updateVisionVector(deltaTime);
974 
975 	const StelProjectorP proj = core->getProjection(StelCore::FrameJ2000);
976 	// the more it is zoomed, the lower the moving speed is (in angle)
977 	double depl=keyMoveSpeed*deltaTime*1000*currentFov;
978 	double deplzoom=keyZoomSpeed*deltaTime*1000*static_cast<double>(proj->deltaZoom(static_cast<float>(currentFov)*(M_PIf/360.0f)))*(360.0/M_PI);
979 
980 	if (flagMoveSlow)
981 	{
982 		depl *= 0.2;
983 		deplzoom *= 0.2;
984 	}
985 
986 	if (deltaAz<0)
987 	{
988 		deltaAz = -depl/30;
989 		if (deltaAz<-0.2)
990 			deltaAz = -0.2;
991 	}
992 	else if (deltaAz>0)
993 	{
994 		deltaAz = (depl/30);
995 		if (deltaAz>0.2)
996 			deltaAz = 0.2;
997 	}
998 
999 	if (deltaAlt<0)
1000 	{
1001 		deltaAlt = -depl/30;
1002 		if (deltaAlt<-0.2)
1003 			deltaAlt = -0.2;
1004 	}
1005 	else if (deltaAlt>0)
1006 	{
1007 		deltaAlt = depl/30;
1008 		if (deltaAlt>0.2)
1009 			deltaAlt = 0.2;
1010 	}
1011 
1012 	if (deltaFov<0)
1013 	{
1014 		deltaFov=qMax(-0.15*currentFov, -deplzoom*5);
1015 		changeFov(deltaFov);
1016 	}
1017 	else if (deltaFov>0)
1018 	{
1019 		deltaFov = qMin(20., deplzoom*5.);
1020 		changeFov(deltaFov);
1021 	}
1022 
1023 	if (flagCustomPan)
1024 	{
1025 		deltaAz  = rateX*M_PI_180*deltaTime;
1026 		deltaAlt = rateY*M_PI_180*deltaTime;
1027 	}
1028 
1029 	panView(deltaAz, deltaAlt);
1030 	updateAutoZoom(deltaTime);
1031 }
1032 
1033 // called at begin of updateMotion()
updateVisionVector(double deltaTime)1034 void StelMovementMgr::updateVisionVector(double deltaTime)
1035 {
1036 	// Specialized setups cannot use this functionality!
1037 	if (flagInhibitAllAutomoves)
1038 		return;
1039 
1040 	if (flagAutoMove)
1041 	{
1042 		if (!move.targetObject.isNull())
1043 		{
1044 			// if zooming in, object may be moving so be sure to zoom to latest position
1045 			// In case we have offset center, we want object still visible in center.
1046 			// Note that if we do not center on an object, we set view direction of the potentially offset screen center!
1047 			// This is by design, to allow accurate setting of display coordinates.
1048 			Vec3d v;
1049 			switch (mountMode)
1050 			{
1051 				case MountAltAzimuthal:
1052 					v = move.targetObject->getAltAzPosAuto(core);
1053 					break;
1054 				case MountEquinoxEquatorial:
1055 					v = move.targetObject->getEquinoxEquatorialPosAuto(core); //  ..Auto! Fix Bug LP:#1484976
1056 					break;
1057 				case MountGalactic:
1058 					v = move.targetObject->getGalacticPos(core);
1059 					break;
1060 				case MountSupergalactic:
1061 					v = move.targetObject->getSupergalacticPos(core);
1062 					break;
1063 				default:
1064 					qWarning() << "StelMovementMgr: unexpected mountMode" << mountMode;
1065 					Q_ASSERT(0);
1066 					v = move.targetObject->getAltAzPosAuto(core); // still do something useful
1067 			}
1068 
1069 			double lat, lon;
1070 			StelUtils::rectToSphe(&lon, &lat, v);
1071 			double altOffset=core->getCurrentStelProjectorParams().viewportCenterOffset[1]*currentFov*M_PI_180;
1072 			lat+=altOffset;
1073 			StelUtils::spheToRect(lon, lat, v);
1074 			move.aim=mountFrameToJ2000(v);
1075 			move.aim.normalize();
1076 			move.aim*=2.;
1077 			// For aiming at objects, we can assume simple up vector.
1078 			move.startUp=getViewUpVectorJ2000();
1079 			move.aimUp=mountFrameToJ2000(Vec3d(0., 0., 1.));
1080 		}
1081 		else // no targetObject:
1082 		{
1083 //			if (move.mountMode == MountAltAzimuthal)
1084 //			{
1085 //				move.startUp=Vec3d(0., 0., 1.);
1086 //				move.aimUp=Vec3d(0., 0., 1.);
1087 //			}
1088 //			else
1089 //			{ // March 2016
1090 				move.startUp=getViewUpVectorJ2000();
1091 				move.aimUp=mountFrameToJ2000(Vec3d(0., 0., 1.));
1092 //			}
1093 		}
1094 		move.coef+=move.speed*static_cast<float>(deltaTime)*1000;
1095 		//qDebug() << "updateVisionVector: setViewUpvectorJ2000 L813";
1096 		setViewUpVectorJ2000(move.aimUp);
1097 		if (move.coef>=1.f)
1098 		{
1099 			//qDebug() << "AutoMove finished. Setting Up vector (in mount frame) to " << upVectorMountFrame.v[0] << "/" << upVectorMountFrame.v[1] << "/" << upVectorMountFrame.v[2];
1100 			flagAutoMove=false;
1101 			move.coef=1.f;
1102 		}
1103 
1104 		// Use a smooth function
1105 		const float smooth = 4.f; // (empirically tested)
1106 		double c;
1107 		switch (zoomingMode){
1108 			case ZoomIn:
1109 				c=(move.coef>.9f ? 1. : 1. - static_cast<double>(powf(1.f-1.11f*move.coef,3.f))); break;
1110 			case ZoomOut:
1111 				// keep in view at first as zoom out
1112 				c=(move.coef<0.1f ? 0. : static_cast<double>(powf(1.11f*(move.coef-.1f),3.f))); break;
1113 			default:
1114 				c = static_cast<double>(std::atan(smooth * 2.f*move.coef-smooth)/std::atan(smooth)/2+0.5f);
1115 		}
1116 
1117 		// 2016-03: In case of azimuthal moves, it is not useful to compute anything from J2000 coordinates.
1118 		// Imagine a slow AltAz move during speedy timelapse: Aim will move!
1119 		// TODO: all variants...
1120 		Vec3d tmpStart;
1121 		Vec3d tmpAim;
1122 		if (move.mountMode==MountAltAzimuthal)
1123 		{
1124 			tmpStart=move.start;
1125 			tmpAim=move.aim;
1126 		}
1127 		else
1128 		{
1129 			tmpStart = j2000ToMountFrame(move.start);
1130 			tmpAim   = j2000ToMountFrame(move.aim);
1131 		}
1132 		double ra_aim, de_aim, ra_start, de_start;
1133 		StelUtils::rectToSphe(&ra_start, &de_start, tmpStart);
1134 		StelUtils::rectToSphe(&ra_aim, &de_aim, tmpAim);
1135 
1136 		// Make sure the position of the object to be aimed at is defined...
1137 		Q_ASSERT(!qIsNaN(move.aim[0]) && !qIsNaN(move.aim[1]) && !qIsNaN(move.aim[2]));
1138 		// Trick to choose the good moving direction and never travel on a distance > PI
1139 		if (ra_aim-ra_start > M_PI)
1140 		{
1141 			ra_aim -= 2.*M_PI;
1142 		}
1143 		else if (ra_aim-ra_start < -M_PI)
1144 		{
1145 			ra_aim += 2.*M_PI;
1146 		}
1147 		const double de_now = de_aim*c + de_start*(1.-c);
1148 		const double ra_now = ra_aim*c + ra_start*(1.-c);
1149 		Vec3d tmp;
1150 		StelUtils::spheToRect(ra_now, de_now, tmp);
1151 		// now tmp is either Mountframe or AltAz interpolated vector.
1152 //		if (move.mountMode==MountAltAzimuthal)
1153 //		{ // Actually, Altaz moves work in Altaz coords only. Maybe we are here?
1154 //		}
1155 //		else
1156 			setViewDirectionJ2000(mountFrameToJ2000(tmp));
1157 //			if (move.mountMode==MountAltAzimuthal)
1158 //			{
1159 //				setViewUpVector(Vec3d(0., 0., 1.));
1160 //				qDebug() << "We do indeed set this";
1161 //			}
1162 		// qDebug() << "setting view direction to " << tmp.v[0] << "/" << tmp.v[1] << "/" << tmp.v[2];
1163 	}
1164 	else // no autoMove
1165 	{
1166 		if (flagTracking && objectMgr->getWasSelected()) // Equatorial vision vector locked on selected object
1167 		{
1168 			Vec3d v;
1169 			switch (mountMode)
1170 			{
1171 				case MountAltAzimuthal:
1172 					v = objectMgr->getSelectedObject()[0]->getAltAzPosAuto(core);
1173 					break;
1174 				case MountEquinoxEquatorial:
1175 					v = objectMgr->getSelectedObject()[0]->getEquinoxEquatorialPosAuto(core);
1176 					break;
1177 				case MountGalactic:
1178 					v = objectMgr->getSelectedObject()[0]->getGalacticPos(core);
1179 					break;
1180 				case MountSupergalactic:
1181 					v = objectMgr->getSelectedObject()[0]->getSupergalacticPos(core);
1182 					break;
1183 				default:
1184 					qWarning() << "StelMovementMgr: unexpected mountMode" << mountMode;
1185 					Q_ASSERT(0);
1186 					v = move.targetObject->getAltAzPosAuto(core); // still do something useful in release build
1187 			}
1188 
1189 			double lat, lon; // general: longitudinal, latitudinal
1190 			StelUtils::rectToSphe(&lon, &lat, v);
1191 			double latOffset=static_cast<double>(core->getCurrentStelProjectorParams().viewportCenterOffset[1]) * currentFov*M_PI_180;
1192 			lat+=latOffset;
1193 			StelUtils::spheToRect(lon, lat, v);
1194 
1195 			setViewDirectionJ2000(mountFrameToJ2000(v));
1196 			//qDebug() << "setViewUpVector() L930";
1197 			setViewUpVectorJ2000(mountFrameToJ2000(Vec3d(0., 0., 1.))); // Does not disturb to reassure this former default.
1198 		}
1199 		else // not tracking or no selection
1200 		{
1201 			if (flagLockEquPos) // Equatorial vision vector locked
1202 			{
1203 				// Recalc local vision vector
1204 				setViewDirectionJ2000(viewDirectionJ2000);
1205 			}
1206 			else
1207 			{
1208 				// Vision vector locked to its position in the mountFrame
1209 				setViewDirectionJ2000(mountFrameToJ2000(viewDirectionMountFrame));
1210 				// After setting time, moveToAltAz broke the up vector without this:
1211 				// Make sure this does not now break zenith views!
1212 				// Or make sure to call moveToAltAz twice.
1213 				//qDebug() << "setUpVectorJ2000 woe L947";
1214 				//setViewUpVectorJ2000(mountFrameToJ2000(Vec3d(0.,0.,1.)));
1215 				setViewUpVectorJ2000(mountFrameToJ2000(upVectorMountFrame)); // maybe fixes? / < < < < < < < < < < THIS WAS THE BIG ONE
1216 			}
1217 		}
1218 	}
1219 }
1220 
deselection(void)1221 void StelMovementMgr::deselection(void)
1222 {
1223 	// Deselect the selected object
1224 	StelApp::getInstance().getStelObjectMgr().unSelect();
1225 	setFlagLockEquPos(false);
1226 	return;
1227 }
1228 
1229 // Go and zoom to the selected object. (Action linked to key, default "/")
autoZoomIn(float moveDuration,bool allowManualZoom)1230 void StelMovementMgr::autoZoomIn(float moveDuration, bool allowManualZoom)
1231 {
1232 	if (!objectMgr->getWasSelected())
1233 		return;
1234 
1235 	moveDuration /= movementsSpeedFactor;
1236 
1237 	float manualMoveDuration;
1238 	if (!getFlagTracking())
1239 	{
1240 		setFlagTracking(true); // includes a call to moveToObject(), but without zooming=1!
1241 		moveToObject(objectMgr->getSelectedObject()[0], moveDuration, ZoomIn);
1242 		manualMoveDuration = moveDuration;
1243 	}
1244 	else
1245 	{
1246 		// faster zoom in manual zoom mode once object is centered
1247 		manualMoveDuration = moveDuration*.66f;
1248 	}
1249 
1250 	if( allowManualZoom && flagManualZoom )
1251 	{
1252 		// if manual zoom mode, user can zoom in incrementally
1253 		double newfov = currentFov*0.5;
1254 		zoomTo(newfov, manualMoveDuration);
1255 	}
1256 	else
1257 	{
1258 		double satfov = objectMgr->getSelectedObject()[0]->getSatellitesFov(core);
1259 
1260 		if (satfov>0.0 && currentFov*0.9>satfov)
1261 			zoomTo(satfov, moveDuration);
1262 		else
1263 		{
1264 			double closefov = objectMgr->getSelectedObject()[0]->getCloseViewFov(core);
1265 			if (currentFov>closefov)
1266 				zoomTo(closefov, moveDuration);
1267 		}
1268 	}
1269 }
1270 
1271 
1272 // Unzoom and go to the init position
autoZoomOut(float moveDuration,bool full)1273 void StelMovementMgr::autoZoomOut(float moveDuration, bool full)
1274 {
1275 	moveDuration /= movementsSpeedFactor;
1276 
1277 	if (objectMgr->getWasSelected() && !full)
1278 	{
1279 		// If the selected object has satellites, unzoom to satellites view
1280 		// unless specified otherwise
1281 		double satfov = objectMgr->getSelectedObject()[0]->getSatellitesFov(core);
1282 
1283 		if (satfov>0.0 && currentFov<=satfov*0.9)
1284 		{
1285 			zoomTo(satfov, moveDuration);
1286 			return;
1287 		}
1288 
1289 		// If the selected object is part of a Planet subsystem (other than sun),
1290 		// unzoom to subsystem view
1291 		satfov = objectMgr->getSelectedObject()[0]->getParentSatellitesFov((core));
1292 		if (satfov>0.0 && currentFov<=satfov*0.9)
1293 		{
1294 			zoomTo(satfov, moveDuration);
1295 			return;
1296 		}
1297 	}
1298 
1299 	zoomTo(initFov, moveDuration);
1300 	if (flagAutoZoomOutResetsDirection)
1301 	{
1302 		moveToJ2000(core->altAzToJ2000(getInitViewingDirection(), StelCore::RefractionOff), mountFrameToJ2000(initViewUp), moveDuration, ZoomOut);
1303 		setFlagTracking(false);
1304 		setFlagLockEquPos(false);
1305 	}
1306 }
1307 
1308 // This is called when you press SPACEBAR: slowly centering&tracking object
setFlagTracking(bool b)1309 void StelMovementMgr::setFlagTracking(bool b)
1310 {
1311 	if (!b || !objectMgr->getWasSelected())
1312 	{
1313 		if(b!=flagTracking)
1314 		{
1315 			flagTracking=false;
1316 			emit flagTrackingChanged(b);
1317 		}
1318 	}
1319 	else
1320 	{
1321 		moveToObject(objectMgr->getSelectedObject()[0], getAutoMoveDuration());
1322 		if(b!=flagTracking)
1323 		{
1324 			flagTracking=true;
1325 			emit flagTrackingChanged(b);
1326 		}
1327 	}
1328 }
1329 
1330 
1331 ////////////////////////////////////////////////////////////////////////////////
1332 // Move to the given J2000 equatorial position
1333 
1334 // aim and aimUp must be in J2000 frame!
moveToJ2000(const Vec3d & aim,const Vec3d & aimUp,float moveDuration,ZoomingMode zooming)1335 void StelMovementMgr::moveToJ2000(const Vec3d& aim, const Vec3d& aimUp, float moveDuration, ZoomingMode zooming)
1336 {
1337 	moveDuration /= movementsSpeedFactor;
1338 
1339 	zoomingMode = zooming;
1340 	move.aim=aim;
1341 	move.aim.normalize();
1342 	move.aim*=2.;
1343 	move.aimUp=aimUp; // the new up vector. We cannot simply keep vertical axis, there may be the intention to look into the zenith or so.
1344 	move.aimUp.normalize();
1345 	move.start=viewDirectionJ2000;
1346 	move.start.normalize();
1347 	move.startUp=getViewUpVectorJ2000();
1348 	move.startUp.normalize();
1349 	move.speed=1.f/(moveDuration*1000);
1350 	move.coef=0.;
1351 	move.targetObject.clear();
1352 	//move.mountMode=mountMode; // Maybe better to have MountEquinoxEquatorial here? ==> YES, fixed orientation problem.
1353 	move.mountMode=MountEquinoxEquatorial;
1354 	flagAutoMove = true;
1355 }
1356 
moveToObject(const StelObjectP & target,float moveDuration,ZoomingMode zooming)1357 void StelMovementMgr::moveToObject(const StelObjectP& target, float moveDuration, ZoomingMode zooming)
1358 {
1359 	moveDuration /= movementsSpeedFactor;
1360 
1361 	zoomingMode = zooming;
1362 	move.aim=Vec3d(0.);
1363 	move.aimUp=mountFrameToJ2000(Vec3d(0., 0., 1.)); // the new up vector. We try simply vertical axis here. (Should be same as pre-0.15)
1364 	move.aimUp.normalize();
1365 	move.start=viewDirectionJ2000;
1366 	move.start.normalize();
1367 	move.startUp=getViewUpVectorJ2000();
1368 	move.startUp.normalize();
1369 	move.speed=1.f/(moveDuration*1000);
1370 	move.coef=0.;
1371 	move.targetObject = target;
1372 	//move.mountMode=mountMode;  // Maybe better to have MountEquinoxEquatorial here? ==> YES, fixed orientation problem.
1373 	move.mountMode=MountEquinoxEquatorial;
1374 	flagAutoMove = true;
1375 }
1376 
1377 // March 2016: This call does nothing when mount frame is not AltAzi! (TODO later: rethink&fix.)
moveToAltAzi(const Vec3d & aim,const Vec3d & aimUp,float moveDuration,ZoomingMode zooming)1378 void StelMovementMgr::moveToAltAzi(const Vec3d& aim, const Vec3d &aimUp, float moveDuration, ZoomingMode zooming)
1379 {
1380 	if (mountMode!=StelMovementMgr::MountAltAzimuthal)
1381 	{
1382 		qDebug() << "StelMovementMgr: called moveToAltAzi, but not in AltAz mount frame. Ignoring.";
1383 		return;
1384 	}
1385 
1386 	moveDuration /= movementsSpeedFactor;
1387 
1388 	// Specify start and aim vectors in AltAz system! Then the auto functions can work it out properly.
1389 	zoomingMode = zooming;
1390 	move.aim=aim;
1391 	move.aim.normalize();
1392 	move.aim*=2.;
1393 	move.aimUp=aimUp; // the new up vector. We cannot simply keep vertical axis, there may be the intention to look into the zenith or so.
1394 	move.aimUp.normalize();
1395 	move.start=core->j2000ToAltAz(viewDirectionJ2000, StelCore::RefractionOff);
1396 	move.start.normalize();
1397 	move.startUp.set(0., 0., 1.);
1398 	move.speed=1.f/(moveDuration*1000);
1399 	move.coef=0.;
1400 	move.targetObject.clear();
1401 	move.mountMode=MountAltAzimuthal; // This signals: start and aim are given in AltAz coordinates.
1402 	flagAutoMove = true;
1403 	//	// debug output if required
1404 	//	double currAlt, currAzi, newAlt, newAzi;
1405 	//	StelUtils::rectToSphe(&currAzi, &currAlt, move.start);
1406 	//	StelUtils::rectToSphe(&newAzi, &newAlt, move.aim);
1407 	//	qDebug() << "StelMovementMgr::moveToAltAzi() from alt:" << currAlt*(180./M_PI) << "/azi" << currAzi*(180./M_PI)  << "to alt:" << newAlt*(180./M_PI)  << "azi" << newAzi*(180./M_PI) ;
1408 }
1409 
1410 
j2000ToMountFrame(const Vec3d & v) const1411 Vec3d StelMovementMgr::j2000ToMountFrame(const Vec3d& v) const
1412 {
1413 	switch (mountMode)
1414 	{
1415 		case MountAltAzimuthal:
1416 			return core->j2000ToAltAz(v, StelCore::RefractionOff); // TODO: Decide if RefractionAuto?
1417 		case MountEquinoxEquatorial:
1418 			return core->j2000ToEquinoxEqu(v, StelCore::RefractionOff);
1419 		case MountGalactic:
1420 			return core->j2000ToGalactic(v);
1421 		case MountSupergalactic:
1422 			return core->j2000ToSupergalactic(v);
1423 	}
1424 	Q_ASSERT(0);
1425 	return Vec3d(0.);
1426 }
1427 
mountFrameToJ2000(const Vec3d & v) const1428 Vec3d StelMovementMgr::mountFrameToJ2000(const Vec3d& v) const
1429 {
1430 	switch (mountMode)
1431 	{
1432 		case MountAltAzimuthal:
1433 			return core->altAzToJ2000(v, StelCore::RefractionOff); // TODO: Decide if RefractionAuto?
1434 		case MountEquinoxEquatorial:
1435 			return core->equinoxEquToJ2000(v, StelCore::RefractionOff);
1436 		case MountGalactic:
1437 			return core->galacticToJ2000(v);
1438 		case MountSupergalactic:
1439 			return core->supergalacticToJ2000(v);
1440 	}
1441 	Q_ASSERT(0);
1442 	return Vec3d(0.);
1443 }
1444 
setViewDirectionJ2000(const Vec3d & v)1445 void StelMovementMgr::setViewDirectionJ2000(const Vec3d& v)
1446 {
1447 	core->lookAtJ2000(v, getViewUpVectorJ2000());
1448 	viewDirectionJ2000 = v;
1449 	viewDirectionMountFrame = j2000ToMountFrame(v);
1450 }
1451 
panView(const double deltaAz,const double deltaAlt)1452 void StelMovementMgr::panView(const double deltaAz, const double deltaAlt)
1453 {
1454 	// DONE 2016-12 FIX UP VECTOR PROBLEM
1455 	// The function is called in update loops, so make a quick check for exit.
1456 	if ((deltaAz==0.) && (deltaAlt==0.))
1457 		return;
1458 
1459 	double azVision, altVision;
1460 	StelUtils::rectToSphe(&azVision,&altVision,j2000ToMountFrame(viewDirectionJ2000));
1461 	// Az is counted from South, eastward.
1462 
1463 	 //qDebug() << "Azimuth:" << azVision * 180./M_PI << "Altitude:" << altVision * 180./M_PI << "Up.X=" << upVectorMountFrame.v[0] << "Up.Y=" << upVectorMountFrame.v[1] << "Up.Z=" << upVectorMountFrame.v[2];
1464 
1465 	// if we are just looking into the pole, azimuth can hopefully be recovered from the customized up vector!
1466 	// When programmatically centering on a pole, we should have set a better up vector for |alt|>0.9*M_PI/2.
1467 	if (fabs(altVision)> 0.95* M_PI_2)
1468 	{
1469 		if (upVectorMountFrame.v[2] < 0.9)
1470 		{
1471 			 //qDebug() << "panView: Recovering azimuth...";
1472 			azVision=atan2(-upVectorMountFrame.v[1], -upVectorMountFrame.v[0]);
1473 			if (altVision < 0.)
1474 				azVision+=M_PI;
1475 		}
1476 		// Remove these lines if all is OK.
1477 //		else
1478 //		{
1479 //			 qDebug() << "panView: UpVector:" << upVectorMountFrame.v[0] << "/" << upVectorMountFrame.v[1] << "/" << upVectorMountFrame.v[2] << "Cannot recover azimuth. Hope it's OK";
1480 //		}
1481 	}
1482 
1483 	// if we are moving in the Azimuthal angle (left/right)
1484 	if (fabs(deltaAz)>1e-10)
1485 		azVision-=deltaAz;
1486 	if (fabs(deltaAlt)>1e-10)
1487 	{
1488 		//if (altVision+deltaAlt <= M_PI_2 && altVision+deltaAlt >= -M_PI_2)
1489 			altVision+=deltaAlt;
1490 		//if (altVision+deltaAlt >  M_PI_2) altVision =  M_PI_2 - 0.000001; // Prevent bug: manual pans (keyboard or mouse!) can never really reach the zenith, but we can accept this.
1491 		//if (altVision+deltaAlt < -M_PI_2) altVision = -M_PI_2 + 0.000001;
1492 		if (altVision >  M_PI_2) altVision =  M_PI_2 - 0.000001; // Prevent bug: manual pans (keyboard or mouse!) can never really reach the zenith, but we can accept this.
1493 		if (altVision < -M_PI_2) altVision = -M_PI_2 + 0.000001;
1494 	}
1495 
1496 	// recalc all the position variables
1497 	if ((fabs(deltaAz)>1e-10) || (fabs(deltaAlt)>1e-10))
1498 	{
1499 		setFlagTracking(false);
1500 		Vec3d tmp;
1501 		StelUtils::spheToRect(azVision, altVision, tmp);
1502 		setViewDirectionJ2000(mountFrameToJ2000(tmp));
1503 		if (fabs(altVision)>0.95*M_PI_2)
1504 		{ // do something about zenith
1505 			setViewUpVector(Vec3d(-cos(azVision), -sin(azVision), 0.) * (altVision>0. ? 1. : -1. ));
1506 		}
1507 		else
1508 		{
1509 			setViewUpVector(Vec3d(0., 0., 1.));
1510 		}
1511 	}
1512 }
1513 
1514 
1515 // Make the first screen position correspond to the second (useful for mouse dragging)
dragView(int x1,int y1,int x2,int y2)1516 void StelMovementMgr::dragView(int x1, int y1, int x2, int y2)
1517 {
1518 	if (dragTimeMode)
1519 	{
1520 		core->setTimeRate(0);
1521 		Vec3d v1, v2;
1522 		const StelProjectorP prj = core->getProjection(StelCore::FrameEquinoxEqu);
1523 		prj->unProject(x2,y2, v2);
1524 		prj->unProject(x1,y1, v1);
1525 		v1[2]=0; v1.normalize();
1526 		v2[2]=0; v2.normalize();
1527 		double angle = (v2^v1)[2];
1528 		double deltaDay = angle/(2.*M_PI)*core->getLocalSiderealDayLength();
1529 		core->setJD(core->getJD()+deltaDay);
1530 		addTimeDragPoint(x2, y2);
1531 	}
1532 	else
1533 	{
1534 		Vec3d tempvec1, tempvec2;
1535 		const StelProjectorP prj = core->getProjection(StelCore::FrameJ2000);
1536 		prj->unProject(x2,y2, tempvec2);
1537 		prj->unProject(x1,y1, tempvec1);
1538 		double az1, alt1, az2, alt2;
1539 		StelUtils::rectToSphe(&az1, &alt1, j2000ToMountFrame(tempvec1));
1540 		StelUtils::rectToSphe(&az2, &alt2, j2000ToMountFrame(tempvec2));
1541 		panView(az2-az1, alt1-alt2);
1542 	}
1543 	setFlagTracking(false);
1544 	setFlagLockEquPos(false);
1545 }
1546 
1547 
1548 // Update autoZoom if activated
updateAutoZoom(double deltaTime)1549 void StelMovementMgr::updateAutoZoom(double deltaTime)
1550 {
1551 	if (flagAutoZoom)
1552 	{
1553 		zoomMove.update(deltaTime);
1554 		double newFov = zoomMove.getValue();
1555 		if (zoomMove.finished())
1556 			flagAutoZoom = 0;
1557 		setFov(newFov); // updates currentFov->don't use newFov later!
1558 
1559 		// In case we have offset center, we want object still visible in center.
1560 		if (flagTracking && objectMgr->getWasSelected()) // vision vector locked on selected object
1561 		{
1562 			Vec3d v, vUp;
1563 			switch (mountMode)
1564 			{
1565 				case MountAltAzimuthal:
1566 					v = objectMgr->getSelectedObject()[0]->getAltAzPosAuto(core);
1567 					break;
1568 				case MountEquinoxEquatorial:
1569 					v = objectMgr->getSelectedObject()[0]->getEquinoxEquatorialPosAuto(core);
1570 					break;
1571 				case MountGalactic:
1572 					v = objectMgr->getSelectedObject()[0]->getGalacticPos(core);
1573 					break;
1574 				case MountSupergalactic:
1575 					v = objectMgr->getSelectedObject()[0]->getSupergalacticPos(core);
1576 					break;
1577 				default:
1578 					qWarning() << "StelMovementMgr: unexpected mountMode" << mountMode;
1579 					Q_ASSERT(0);
1580 			}
1581 
1582 			double lat, lon; // general: longitudinal, latitudinal
1583 			StelUtils::rectToSphe(&lon, &lat, v); // guaranteed to be normalized.
1584 			// vUp could usually be (0/0/1) in most cases, unless |lat|==pi/2. We MUST build an adequate Up vector!
1585 			if (fabs(lat)>0.9*M_PI_2)
1586 			{
1587 				vUp = Vec3d(-cos(lon), -sin(lon), 0.) * (lat>0. ? 1. : -1. );
1588 			}
1589 			else
1590 				vUp.set(0.,0.,1.);
1591 			double latOffset=static_cast<double>(core->getCurrentStelProjectorParams().viewportCenterOffset[1])*currentFov*M_PI_180;
1592 			lat+=latOffset;
1593 			StelUtils::spheToRect(lon, lat, v);
1594 
1595 			if (flagAutoMove)
1596 			{
1597 				move.aim=mountFrameToJ2000(v);
1598 				move.aim.normalize();
1599 				move.aim*=2.;
1600 				move.aimUp=mountFrameToJ2000(vUp);
1601 				move.aimUp.normalize();
1602 			}
1603 			else
1604 			{
1605 				setViewDirectionJ2000(mountFrameToJ2000(v));
1606 				//qDebug() << "setViewUpVector L1501";
1607 				setViewUpVectorJ2000(mountFrameToJ2000(vUp));
1608 			}
1609 		}
1610 	}
1611 }
1612 
1613 // Zoom to the given field of view
zoomTo(double aim_fov,float zoomDuration)1614 void StelMovementMgr::zoomTo(double aim_fov, float zoomDuration)
1615 {
1616 	zoomDuration /= movementsSpeedFactor;
1617 	zoomMove.setTarget(currentFov, aim_fov, zoomDuration);
1618 	flagAutoZoom = true;
1619 }
1620 
changeFov(double deltaFov)1621 void StelMovementMgr::changeFov(double deltaFov)
1622 {
1623 	// if we are zooming in or out
1624 	if (fabs(deltaFov)>0)
1625 		setFov(currentFov + deltaFov);
1626 }
1627 
getAimFov(void) const1628 double StelMovementMgr::getAimFov(void) const
1629 {
1630 	return (flagAutoZoom ? zoomMove.getAim() : currentFov);
1631 }
1632 
1633 // This is called e.g. when projection changes.
1634 // We clamp this to the user-set user_maxFov (e.g. for planetarium: 180°; GH #1836)
setMaxFov(double max)1635 void StelMovementMgr::setMaxFov(double max)
1636 {
1637 	maxFov = qMin(max, userMaxFov);
1638 	if (currentFov > maxFov)
1639 	{
1640 		setFov(maxFov);
1641 	}
1642 }
1643 
setUserMaxFov(double max)1644 void StelMovementMgr::setUserMaxFov(double max)
1645 {
1646 	userMaxFov = qMin(360., max);
1647 	if (maxFov>userMaxFov)
1648 		setMaxFov(userMaxFov);
1649 	else
1650 	{
1651 		const float prjMaxFov = StelApp::getInstance().getCore()->getProjection(StelProjector::ModelViewTranformP(new StelProjector::Mat4dTransform(Mat4d::identity())))->getMaxFov();
1652 		setMaxFov(qMin(userMaxFov, static_cast<double>(prjMaxFov)));
1653 	}
1654 	emit userMaxFovChanged(userMaxFov);
1655 }
1656 
moveViewport(double offsetX,double offsetY,const float duration)1657 void StelMovementMgr::moveViewport(double offsetX, double offsetY, const float duration)
1658 {
1659 	//clamp to valid range
1660 	offsetX = qBound(-50., offsetX, 50.);
1661 	offsetY = qBound(-50., offsetY, 50.);
1662 
1663 	Vec2d oldTargetViewportOffset = targetViewportOffset;
1664 	targetViewportOffset.set(offsetX, offsetY);
1665 
1666 	if(fabs(offsetX - oldTargetViewportOffset[0]) > 1e-10)
1667 		emit viewportHorizontalOffsetTargetChanged(offsetX);
1668 	if(fabs(offsetY - oldTargetViewportOffset[1]) > 1e-10)
1669 		emit viewportVerticalOffsetTargetChanged(offsetY);
1670 
1671 	if (duration<=0.0f)
1672 	{
1673 		//avoid using the timeline to minimize overhead
1674 		core->setViewportOffset(offsetX, offsetY);
1675 		return;
1676 	}
1677 
1678 	// Frame will now be 0..100, and we must interpolate in handleViewportOffsetMovement(frame) between old and new offsets.
1679 	oldViewportOffset.set(core->getViewportHorizontalOffset(), core->getViewportVerticalOffset());
1680 
1681 	viewportOffsetTimeline->stop();
1682 	viewportOffsetTimeline->setDuration(static_cast<int>(1000.f*duration));
1683 
1684 	//qDebug() << "moveViewport() started, from " << oldViewportOffset.v[0] << "/" << oldViewportOffset.v[1] << " towards " << offsetX << "/" << offsetY;
1685 	viewportOffsetTimeline->start();
1686 }
1687 
1688 // slot which is connected to the viewportOffsetTimeline and does the actual updates.
handleViewportOffsetMovement(qreal value)1689 void StelMovementMgr::handleViewportOffsetMovement(qreal value)
1690 {
1691 	// value is always 0...1
1692 	double offsetX=oldViewportOffset.v[0] + (targetViewportOffset.v[0]-oldViewportOffset.v[0])*value;
1693 	double offsetY=oldViewportOffset.v[1] + (targetViewportOffset.v[1]-oldViewportOffset.v[1])*value;
1694 	//qDebug() << "handleViewportOffsetMovement(" << value << "): Setting viewport offset to " << offsetX << "/" << offsetY;
1695 	core->setViewportOffset(offsetX, offsetY);
1696 }
1697 
smoothPan(double deltaX,double deltaY,double ptime,bool s)1698 void StelMovementMgr::smoothPan(double deltaX, double deltaY, double ptime, bool s)
1699 {
1700 	flagCustomPan = s;
1701 	if (s)
1702 	{
1703 		rateX = deltaX/ptime; // degrees per second
1704 		rateY = deltaY/ptime; // degrees per second
1705 		setFlagTracking(false);
1706 		setFlagLockEquPos(false);
1707 	}
1708 	else
1709 		deltaAz = deltaAlt = 0.0;
1710 }
1711 
1712