1 /*
2  * Stellarium
3  * Copyright (C) 2002 Fabien Chereau
4  * Copyright (C) 2010 Bogdan Marinov
5  * Copyright (C) 2011 Alexander Wolf
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335, USA.
20  */
21 
22 #include "SolarSystem.hpp"
23 #include "StelTexture.hpp"
24 #include "EphemWrapper.hpp"
25 #include "Orbit.hpp"
26 
27 #include "StelProjector.hpp"
28 #include "StelApp.hpp"
29 #include "StelCore.hpp"
30 #include "StelTextureMgr.hpp"
31 #include "StelObjectMgr.hpp"
32 #include "StelLocaleMgr.hpp"
33 #include "StelSkyCultureMgr.hpp"
34 #include "StelFileMgr.hpp"
35 #include "StelModuleMgr.hpp"
36 #include "StelIniParser.hpp"
37 #include "Planet.hpp"
38 #include "MinorPlanet.hpp"
39 #include "Comet.hpp"
40 #include "StelMainView.hpp"
41 
42 #include "StelSkyDrawer.hpp"
43 #include "StelUtils.hpp"
44 #include "StelPainter.hpp"
45 #include "TrailGroup.hpp"
46 #include "RefractionExtinction.hpp"
47 
48 #include "AstroCalcDialog.hpp"
49 #include "StelObserver.hpp"
50 
51 #include <functional>
52 #include <algorithm>
53 
54 #include <QTextStream>
55 #include <QSettings>
56 #include <QVariant>
57 #include <QString>
58 #include <QStringList>
59 #include <QMap>
60 #include <QMultiMap>
61 #include <QMapIterator>
62 #include <QDebug>
63 #include <QDir>
64 #include <QHash>
65 
SolarSystem()66 SolarSystem::SolarSystem() : StelObjectModule()
67 	, shadowPlanetCount(0)
68 	, earthShadowEnlargementDanjon(false)
69 	, flagMoonScale(false)
70 	, moonScale(1.0)
71 	, flagMinorBodyScale(false)
72 	, minorBodyScale(1.0)
73 	, flagPlanetScale(false)
74 	, planetScale(1.0)
75 	, flagSunScale(false)
76 	, sunScale(1.0)
77 	, labelsAmount(false)
78 	, flagPermanentSolarCorona(true)
79 	, flagOrbits(false)
80 	, flagLightTravelTime(true)
81 	, flagUseObjModels(false)
82 	, flagShowObjSelfShadows(true)
83 	, flagShow(false)
84 	, flagPointer(false)
85 	, flagNativePlanetNames(false)
86 	, flagIsolatedTrails(true)
87 	, numberIsolatedTrails(0)
88 	, maxTrailPoints(5000)
89 	, maxTrailTimeExtent(1)
90 	, trailsThickness(1)
91 	, flagIsolatedOrbits(true)
92 	, flagPlanetsOrbitsOnly(false)
93 	, ephemerisMarkersDisplayed(true)
94 	, ephemerisDatesDisplayed(false)
95 	, ephemerisMagnitudesDisplayed(false)
96 	, ephemerisHorizontalCoordinates(false)
97 	, ephemerisLineDisplayed(false)
98 	, ephemerisAlwaysOn(false)
99 	, ephemerisLineThickness(1)
100 	, ephemerisSkipDataDisplayed(false)
101 	, ephemerisSkipMarkersDisplayed(false)
102 	, ephemerisDataStep(1)
103 	, ephemerisDataLimit(1)
104 	, ephemerisSmartDatesDisplayed(true)
105 	, ephemerisScaleMarkersDisplayed(false)
106 	, ephemerisGenericMarkerColor(Vec3f(1.0f, 1.0f, 0.0f))
107 	, ephemerisSecondaryMarkerColor(Vec3f(0.7f, 0.7f, 1.0f))
108 	, ephemerisSelectedMarkerColor(Vec3f(1.0f, 0.7f, 0.0f))
109 	, ephemerisMercuryMarkerColor(Vec3f(1.0f, 1.0f, 0.0f))
110 	, ephemerisVenusMarkerColor(Vec3f(1.0f, 1.0f, 1.0f))
111 	, ephemerisMarsMarkerColor(Vec3f(1.0f, 0.0f, 0.0f))
112 	, ephemerisJupiterMarkerColor(Vec3f(0.3f, 1.0f, 1.0f))
113 	, ephemerisSaturnMarkerColor(Vec3f(0.0f, 1.0f, 0.0f))
114 	, allTrails(Q_NULLPTR)
115 	, conf(StelApp::getInstance().getSettings())
116 {
117 	planetNameFont.setPixelSize(StelApp::getInstance().getScreenFontSize());
118 	connect(&StelApp::getInstance(), SIGNAL(screenFontSizeChanged(int)), this, SLOT(setFontSize(int)));
119 	setObjectName("SolarSystem");
120 }
121 
setFontSize(int newFontSize)122 void SolarSystem::setFontSize(int newFontSize)
123 {
124 	planetNameFont.setPixelSize(newFontSize);
125 }
126 
~SolarSystem()127 SolarSystem::~SolarSystem()
128 {
129 	// release selected:
130 	selected.clear();
131 	selectedSSO.clear();
132 	for (auto* orb : qAsConst(orbits))
133 	{
134 		delete orb;
135 		orb = Q_NULLPTR;
136 	}
137 	sun.clear();
138 	moon.clear();
139 	earth.clear();
140 	Planet::hintCircleTex.clear();
141 	Planet::texEarthShadow.clear();
142 
143 	texEphemerisMarker.clear();
144 	texEphemerisCometMarker.clear();
145 	texPointer.clear();
146 
147 	delete allTrails;
148 	allTrails = Q_NULLPTR;
149 
150 	// Get rid of circular reference between the shared pointers which prevent proper destruction of the Planet objects.
151 	for (const auto& p : qAsConst(systemPlanets))
152 	{
153 		p->satellites.clear();
154 	}
155 
156 	//delete comet textures created in loadPlanets
157 	Comet::comaTexture.clear();
158 	Comet::tailTexture.clear();
159 
160 	//deinit of SolarSystem is NOT called at app end automatically
161 	deinit();
162 }
163 
164 /*************************************************************************
165  Re-implementation of the getCallOrder method
166 *************************************************************************/
getCallOrder(StelModuleActionName actionName) const167 double SolarSystem::getCallOrder(StelModuleActionName actionName) const
168 {
169 	if (actionName==StelModule::ActionDraw)
170 		return StelApp::getInstance().getModuleMgr().getModule("StarMgr")->getCallOrder(actionName)+10;
171 	return 0;
172 }
173 
174 // Init and load the solar system data
init()175 void SolarSystem::init()
176 {
177 	Q_ASSERT(conf);
178 
179 	Planet::init();
180 	loadPlanets();	// Load planets data
181 
182 	// Compute position and matrix of sun and all the satellites (ie planets)
183 	// for the first initialization Q_ASSERT that center is sun center (only impacts on light speed correction)
184 	computePositions(StelApp::getInstance().getCore()->getJDE(), getSun());
185 
186 	setSelected("");	// Fix a bug on macosX! Thanks Fumio!
187 	setFlagDrawMoonHalo(conf->value("viewing/flag_draw_moon_halo", true).toBool());
188 	setFlagDrawSunHalo(conf->value("viewing/flag_draw_sun_halo", true).toBool());
189 	setFlagMoonScale(conf->value("viewing/flag_moon_scaled", conf->value("viewing/flag_init_moon_scaled", false).toBool()).toBool());  // name change
190 	setMoonScale(conf->value("viewing/moon_scale", 4.0).toDouble());
191 	setMinorBodyScale(conf->value("viewing/minorbodies_scale", 10.0).toDouble());
192 	setFlagMinorBodyScale(conf->value("viewing/flag_minorbodies_scaled", false).toBool());
193 	setFlagPlanetScale(conf->value("viewing/flag_planets_scaled", false).toBool());
194 	setPlanetScale(conf->value("viewing/planets_scale", 150.0).toDouble());
195 	setFlagSunScale(conf->value("viewing/flag_sun_scaled", false).toBool());
196 	setSunScale(conf->value("viewing/sun_scale", 4.0).toDouble());
197 	setFlagPlanets(conf->value("astro/flag_planets").toBool());
198 	setFlagHints(conf->value("astro/flag_planets_hints").toBool());
199 	setFlagLabels(conf->value("astro/flag_planets_labels", true).toBool());
200 	setLabelsAmount(conf->value("astro/labels_amount", 3.).toDouble());
201 	setFlagOrbits(conf->value("astro/flag_planets_orbits").toBool());
202 	setFlagLightTravelTime(conf->value("astro/flag_light_travel_time", true).toBool());
203 	setFlagUseObjModels(conf->value("astro/flag_use_obj_models", false).toBool());
204 	setFlagShowObjSelfShadows(conf->value("astro/flag_show_obj_self_shadows", true).toBool());
205 	setFlagPointer(conf->value("astro/flag_planets_pointers", true).toBool());
206 	// Set the algorithm from Astronomical Almanac for computation of apparent magnitudes for
207 	// planets in case  observer on the Earth by default
208 	setApparentMagnitudeAlgorithmOnEarth(conf->value("astro/apparent_magnitude_algorithm", "Mallama2018").toString());
209 	setFlagNativePlanetNames(conf->value("viewing/flag_planets_native_names", true).toBool());
210 	// Is enabled the showing of isolated trails for selected objects only?
211 	setFlagIsolatedTrails(conf->value("viewing/flag_isolated_trails", true).toBool());
212 	setNumberIsolatedTrails(conf->value("viewing/number_isolated_trails", 1).toInt());
213 	setMaxTrailPoints(conf->value("viewing/max_trail_points", 5000).toInt());
214 	setMaxTrailTimeExtent(conf->value("viewing/max_trail_time_extent", 1).toInt());
215 	setFlagIsolatedOrbits(conf->value("viewing/flag_isolated_orbits", true).toBool());
216 	setFlagPlanetsOrbitsOnly(conf->value("viewing/flag_planets_orbits_only", false).toBool());
217 	setFlagPermanentOrbits(conf->value("astro/flag_permanent_orbits", false).toBool());
218 	setOrbitColorStyle(conf->value("astro/planets_orbits_color_style", "one_color").toString());
219 
220 	// Settings for calculation of position of Great Red Spot on Jupiter
221 	setFlagCustomGrsSettings(conf->value("astro/flag_grs_custom", false).toBool());
222 	setCustomGrsLongitude(conf->value("astro/grs_longitude", 216).toInt());
223 	setCustomGrsDrift(conf->value("astro/grs_drift", 15.).toDouble());
224 	setCustomGrsJD(conf->value("astro/grs_jd", 2456901.5).toDouble());
225 
226 	setFlagEarthShadowEnlargementDanjon(conf->value("astro/shadow_enlargement_danjon", false).toBool());
227 	setFlagPermanentSolarCorona(conf->value("viewing/flag_draw_sun_corona", true).toBool());
228 
229 	// Load colors from config file
230 	QString defaultColor = conf->value("color/default_color").toString();
231 	setLabelsColor(                    Vec3f(conf->value("color/planet_names_color", defaultColor).toString()));
232 	setOrbitsColor(                    Vec3f(conf->value("color/sso_orbits_color", defaultColor).toString()));
233 	setMajorPlanetsOrbitsColor(        Vec3f(conf->value("color/major_planet_orbits_color", "0.7,0.2,0.2").toString()));
234 	setMoonsOrbitsColor(               Vec3f(conf->value("color/moon_orbits_color", "0.7,0.2,0.2").toString()));
235 	setMinorPlanetsOrbitsColor(        Vec3f(conf->value("color/minor_planet_orbits_color", "0.7,0.5,0.5").toString()));
236 	setDwarfPlanetsOrbitsColor(        Vec3f(conf->value("color/dwarf_planet_orbits_color", "0.7,0.5,0.5").toString()));
237 	setCubewanosOrbitsColor(           Vec3f(conf->value("color/cubewano_orbits_color", "0.7,0.5,0.5").toString()));
238 	setPlutinosOrbitsColor(            Vec3f(conf->value("color/plutino_orbits_color", "0.7,0.5,0.5").toString()));
239 	setScatteredDiskObjectsOrbitsColor(Vec3f(conf->value("color/sdo_orbits_color", "0.7,0.5,0.5").toString()));
240 	setOortCloudObjectsOrbitsColor(    Vec3f(conf->value("color/oco_orbits_color", "0.7,0.5,0.5").toString()));
241 	setCometsOrbitsColor(              Vec3f(conf->value("color/comet_orbits_color", "0.7,0.8,0.8").toString()));
242 	setSednoidsOrbitsColor(            Vec3f(conf->value("color/sednoid_orbits_color", "0.7,0.5,0.5").toString()));
243 	setInterstellarOrbitsColor(        Vec3f(conf->value("color/interstellar_orbits_color", "1.0,0.6,1.0").toString()));
244 	setMercuryOrbitColor(              Vec3f(conf->value("color/mercury_orbit_color", "0.5,0.5,0.5").toString()));
245 	setVenusOrbitColor(                Vec3f(conf->value("color/venus_orbit_color", "0.9,0.9,0.7").toString()));
246 	setEarthOrbitColor(                Vec3f(conf->value("color/earth_orbit_color", "0.0,0.0,1.0").toString()));
247 	setMarsOrbitColor(                 Vec3f(conf->value("color/mars_orbit_color", "0.8,0.4,0.1").toString()));
248 	setJupiterOrbitColor(              Vec3f(conf->value("color/jupiter_orbit_color", "1.0,0.6,0.0").toString()));
249 	setSaturnOrbitColor(               Vec3f(conf->value("color/saturn_orbit_color", "1.0,0.8,0.0").toString()));
250 	setUranusOrbitColor(               Vec3f(conf->value("color/uranus_orbit_color", "0.0,0.7,1.0").toString()));
251 	setNeptuneOrbitColor(              Vec3f(conf->value("color/neptune_orbit_color", "0.0,0.3,1.0").toString()));
252 	setTrailsColor(                    Vec3f(conf->value("color/object_trails_color", defaultColor).toString()));
253 	setPointerColor(                   Vec3f(conf->value("color/planet_pointers_color", "1.0,0.3,0.3").toString()));
254 
255 	// Ephemeris stuff
256 	setFlagEphemerisMarkers(conf->value("astrocalc/flag_ephemeris_markers", true).toBool());
257 	setFlagEphemerisAlwaysOn(conf->value("astrocalc/flag_ephemeris_alwayson", true).toBool());
258 	setFlagEphemerisDates(conf->value("astrocalc/flag_ephemeris_dates", false).toBool());
259 	setFlagEphemerisMagnitudes(conf->value("astrocalc/flag_ephemeris_magnitudes", false).toBool());
260 	setFlagEphemerisHorizontalCoordinates(conf->value("astrocalc/flag_ephemeris_horizontal", false).toBool());
261 	setFlagEphemerisLine(conf->value("astrocalc/flag_ephemeris_line", false).toBool());
262 	setEphemerisLineThickness(conf->value("astrocalc/ephemeris_line_thickness", 1).toInt());
263 	setFlagEphemerisSkipData(conf->value("astrocalc/flag_ephemeris_skip_data", false).toBool());
264 	setFlagEphemerisSkipMarkers(conf->value("astrocalc/flag_ephemeris_skip_markers", false).toBool());
265 	setEphemerisDataStep(conf->value("astrocalc/ephemeris_data_step", 1).toInt());
266 	setFlagEphemerisSmartDates(conf->value("astrocalc/flag_ephemeris_smart_dates", true).toBool());
267 	setFlagEphemerisScaleMarkers(conf->value("astrocalc/flag_ephemeris_scale_markers", false).toBool());
268 	setEphemerisGenericMarkerColor( Vec3f(conf->value("color/ephemeris_generic_marker_color", "1.0,1.0,0.0").toString()));
269 	setEphemerisSecondaryMarkerColor( Vec3f(conf->value("color/ephemeris_secondary_marker_color", "0.7,0.7,1.0").toString()));
270 	setEphemerisSelectedMarkerColor(Vec3f(conf->value("color/ephemeris_selected_marker_color", "1.0,0.7,0.0").toString()));
271 	setEphemerisMercuryMarkerColor( Vec3f(conf->value("color/ephemeris_mercury_marker_color", "1.0,1.0,0.0").toString()));
272 	setEphemerisVenusMarkerColor(   Vec3f(conf->value("color/ephemeris_venus_marker_color", "1.0,1.0,1.0").toString()));
273 	setEphemerisMarsMarkerColor(    Vec3f(conf->value("color/ephemeris_mars_marker_color", "1.0,0.0,0.0").toString()));
274 	setEphemerisJupiterMarkerColor( Vec3f(conf->value("color/ephemeris_jupiter_marker_color", "0.3,1.0,1.0").toString()));
275 	setEphemerisSaturnMarkerColor(  Vec3f(conf->value("color/ephemeris_saturn_marker_color", "0.0,1.0,0.0").toString()));
276 
277 	setOrbitsThickness(conf->value("astro/object_orbits_thickness", 1).toBool());
278 	setTrailsThickness(conf->value("astro/object_trails_thickness", 1).toBool());
279 	recreateTrails();
280 	setFlagTrails(conf->value("astro/flag_object_trails", false).toBool());
281 
282 	StelObjectMgr *objectManager = GETSTELMODULE(StelObjectMgr);
283 	objectManager->registerStelObjectMgr(this);
284 	connect(objectManager, SIGNAL(selectedObjectChanged(StelModule::StelModuleSelectAction)),
285 		this, SLOT(selectedObjectChange(StelModule::StelModuleSelectAction)));
286 
287 	texPointer = StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/pointeur4.png");
288 	texEphemerisMarker = StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/disk.png");
289 	texEphemerisCometMarker = StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/cometIcon.png");
290 	Planet::hintCircleTex = StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/planet-indicator.png");
291 
292 	StelApp *app = &StelApp::getInstance();
293 	connect(app, SIGNAL(languageChanged()), this, SLOT(updateI18n()));
294 	connect(&app->getSkyCultureMgr(), SIGNAL(currentSkyCultureChanged(QString)), this, SLOT(updateSkyCulture(QString)));
295 	connect(&StelMainView::getInstance(), SIGNAL(reloadShadersRequested()), this, SLOT(reloadShaders()));
296 	StelCore *core = app->getCore();
297 	connect(core, SIGNAL(locationChanged(StelLocation)), this, SLOT(recreateTrails()));
298 	connect(core, SIGNAL(dateChangedForTrails()), this, SLOT(recreateTrails()));
299 
300 	QString displayGroup = N_("Display Options");
301 	addAction("actionShow_Planets", displayGroup, N_("Planets"), "planetsDisplayed", "P");
302 	addAction("actionShow_Planets_Labels", displayGroup, N_("Planet labels"), "labelsDisplayed", "Alt+P");
303 	addAction("actionShow_Planets_Orbits", displayGroup, N_("Planet orbits"), "flagOrbits", "O");
304 	addAction("actionShow_Planets_Trails", displayGroup, N_("Planet trails"), "trailsDisplayed", "Shift+T");
305 	addAction("actionShow_Planets_Trails_Reset", displayGroup, N_("Planet trails reset"), "recreateTrails()"); // No hotkey predefined.
306 	//there is a small discrepancy in the GUI: "Show planet markers" actually means show planet hints
307 	addAction("actionShow_Planets_Hints", displayGroup, N_("Planet markers"), "flagHints", "Ctrl+P");
308 	addAction("actionShow_Planets_Pointers", displayGroup, N_("Planet selection marker"), "flagPointer", "Ctrl+Shift+P");
309 	addAction("actionShow_Planets_EnlargeMoon", displayGroup, N_("Enlarge Moon"), "flagMoonScale");
310 	addAction("actionShow_Planets_EnlargeMinor", displayGroup, N_("Enlarge minor bodies"), "flagMinorBodyScale");
311 	addAction("actionShow_Planets_EnlargePlanets", displayGroup, N_("Enlarge Planets"), "flagPlanetScale");
312 	addAction("actionShow_Planets_EnlargeSun", displayGroup, N_("Enlarge Sun"), "flagSunScale");
313 	addAction("actionShow_Skyculture_NativePlanetNames", displayGroup, N_("Native planet names (from starlore)"), "flagNativePlanetNames", "Ctrl+Shift+N");
314 
315 	connect(StelApp::getInstance().getModule("HipsMgr"), SIGNAL(gotNewSurvey(HipsSurveyP)),
316 			this, SLOT(onNewSurvey(HipsSurveyP)));
317 
318 	// Fill ephemeris dates
319 	connect(this, SIGNAL(requestEphemerisVisualization()), this, SLOT(fillEphemerisDates()));
320 	connect(this, SIGNAL(ephemerisDataStepChanged(int)), this, SLOT(fillEphemerisDates()));
321 	connect(this, SIGNAL(ephemerisSkipDataChanged(bool)), this, SLOT(fillEphemerisDates()));
322 	connect(this, SIGNAL(ephemerisSkipMarkersChanged(bool)), this, SLOT(fillEphemerisDates()));
323 	connect(this, SIGNAL(ephemerisSmartDatesChanged(bool)), this, SLOT(fillEphemerisDates()));
324 }
325 
deinit()326 void SolarSystem::deinit()
327 {
328 	Planet::deinitShader();
329 	Planet::deinitFBO();
330 }
331 
resetTextures(const QString & planetName)332 void SolarSystem::resetTextures(const QString &planetName)
333 {
334 	if (planetName.isEmpty())
335 	{
336 		for (const auto& p : qAsConst(systemPlanets))
337 		{
338 			p->resetTextures();
339 		}
340 	}
341 	else
342 	{
343 		PlanetP planet = searchByEnglishName(planetName);
344 		if (!planet.isNull())
345 			planet->resetTextures();
346 	}
347 
348 }
349 
setTextureForPlanet(const QString & planetName,const QString & texName)350 void SolarSystem::setTextureForPlanet(const QString& planetName, const QString& texName)
351 {
352 	PlanetP planet = searchByEnglishName(planetName);
353 	if (!planet.isNull())
354 		planet->replaceTexture(texName);
355 	else
356 		qWarning() << "The planet" << planetName << "was not found. Please check the name.";
357 }
358 
recreateTrails()359 void SolarSystem::recreateTrails()
360 {
361 	// Create a trail group containing all the planets orbiting the sun (not including satellites)
362 	if (allTrails!=Q_NULLPTR)
363 		delete allTrails;
364 	allTrails = new TrailGroup(maxTrailTimeExtent * 365.f, maxTrailPoints);
365 
366 	unsigned long cnt = static_cast<unsigned long>(selectedSSO.size());
367 	if (cnt>0 && getFlagIsolatedTrails())
368 	{
369 		unsigned long limit = static_cast<unsigned long>(getNumberIsolatedTrails());
370 		if (cnt<limit)
371 			limit = cnt;
372 		for (unsigned long i=0; i<limit; i++)
373 		{
374 			if (selectedSSO[cnt - i - 1]->getPlanetType() != Planet::isObserver)
375 				allTrails->addObject(static_cast<QSharedPointer<StelObject>>(selectedSSO[cnt - i - 1]), &trailsColor);
376 		}
377 	}
378 	else
379 	{
380 		for (const auto& p : getSun()->satellites)
381 		{
382 			if (p->getPlanetType() != Planet::isObserver)
383 				allTrails->addObject(static_cast<QSharedPointer<StelObject>>(p), &trailsColor);
384 		}
385 		// Add moons of current planet
386 		StelCore *core=StelApp::getInstance().getCore();
387 		const StelObserver *obs=core->getCurrentObserver();
388 		if (obs)
389 		{
390 			const QSharedPointer<Planet> planet=obs->getHomePlanet();
391 			for (const auto& m : planet->satellites)
392 				if (m->getPlanetType() != Planet::isObserver)
393 					allTrails->addObject(static_cast<QSharedPointer<StelObject>>(m), &trailsColor);
394 		}
395 	}
396 }
397 
398 
updateSkyCulture(const QString & skyCultureDir)399 void SolarSystem::updateSkyCulture(const QString& skyCultureDir)
400 {
401 	planetNativeNamesMap.clear();
402 	planetNativeNamesMeaningMap.clear();
403 
404 	QString namesFile = StelFileMgr::findFile("skycultures/" + skyCultureDir + "/planet_names.fab");
405 
406 	if (namesFile.isEmpty())
407 	{
408 		for (const auto& p : qAsConst(systemPlanets))
409 		{
410 			if (p->getPlanetType()==Planet::isPlanet || p->getPlanetType()==Planet::isMoon || p->getPlanetType()==Planet::isStar)
411 			{
412 				p->setNativeName("");
413 				p->setNativeNameMeaning("");
414 			}
415 		}
416 		updateI18n();
417 		return;
418 	}
419 
420 	// Open file
421 	QFile planetNamesFile(namesFile);
422 	if (!planetNamesFile.open(QIODevice::ReadOnly | QIODevice::Text))
423 	{
424 		qDebug() << " Cannot open file" << QDir::toNativeSeparators(namesFile);
425 		return;
426 	}
427 
428 	// Now parse the file
429 	// lines to ignore which start with a # or are empty
430 	QRegularExpression commentRx("^(\\s*#.*|\\s*)$");
431 
432 	// lines which look like records - we use the RE to extract the fields
433 	// which will be available in recRx.capturedTexts()
434 	QRegularExpression recRx("^\\s*(\\w+)\\s+\"(.+)\"\\s+_[(]\"(.+)\"[)]\\n");
435 
436 	QString record, planetId, nativeName, nativeNameMeaning;
437 
438 	// keep track of how many records we processed.
439 	int totalRecords=0;
440 	int readOk=0;
441 	int lineNumber=0;
442 	while (!planetNamesFile.atEnd())
443 	{
444 		record = QString::fromUtf8(planetNamesFile.readLine());
445 		lineNumber++;
446 
447 		// Skip comments
448 		if (commentRx.match(record).hasMatch())
449 			continue;
450 
451 		totalRecords++;
452 
453 		QRegularExpressionMatch match=recRx.match(record);
454 		if (!match.hasMatch())
455 		{
456 			qWarning() << "ERROR - cannot parse record at line" << lineNumber << "in planet names file" << QDir::toNativeSeparators(namesFile);
457 		}
458 		else
459 		{
460 			planetId          = match.captured(1).trimmed();
461 			nativeName        = match.captured(2).trimmed();
462 			nativeNameMeaning = match.captured(3).trimmed();
463 			planetNativeNamesMap[planetId] = nativeName;
464 			planetNativeNamesMeaningMap[planetId] = nativeNameMeaning;
465 			readOk++;
466 		}
467 	}
468 	planetNamesFile.close();
469 	qDebug() << "Loaded" << readOk << "/" << totalRecords << "native names of planets";
470 
471 	for (const auto& p : qAsConst(systemPlanets))
472 	{
473 		if (p->getPlanetType()==Planet::isPlanet || p->getPlanetType()==Planet::isMoon || p->getPlanetType()==Planet::isStar)
474 		{
475 			p->setNativeName(planetNativeNamesMap[p->getEnglishName()]);
476 			p->setNativeNameMeaning(planetNativeNamesMeaningMap[p->getEnglishName()]);
477 		}
478 	}
479 
480 	updateI18n();
481 }
482 
reloadShaders()483 void SolarSystem::reloadShaders()
484 {
485 	Planet::deinitShader();
486 	Planet::initShader();
487 }
488 
drawPointer(const StelCore * core)489 void SolarSystem::drawPointer(const StelCore* core)
490 {
491 	const StelProjectorP prj = core->getProjection(StelCore::FrameJ2000);
492 
493 	const QList<StelObjectP> newSelected = GETSTELMODULE(StelObjectMgr)->getSelectedObject("Planet");
494 	if (!newSelected.empty())
495 	{
496 		const StelObjectP obj = newSelected[0];
497 		Vec3d pos=obj->getJ2000EquatorialPos(core);
498 
499 		Vec3d screenpos;
500 		// Compute 2D pos and return if outside screen
501 		if (!prj->project(pos, screenpos))
502 			return;
503 
504 		StelPainter sPainter(prj);
505 		sPainter.setColor(getPointerColor());
506 
507 		double size = obj->getAngularSize(core)*M_PI_180*prj->getPixelPerRadAtCenter()*2.;
508 
509 		const double scale = prj->getDevicePixelsPerPixel()*StelApp::getInstance().getGlobalScalingRatio();
510 		size+= scale * (45. + 10.*std::sin(2. * StelApp::getInstance().getAnimationTime()));
511 
512 		texPointer->bind();
513 
514 		sPainter.setBlending(true);
515 
516 		size*=0.5;
517 		const double angleBase = StelApp::getInstance().getAnimationTime() * 10;
518 		// We draw 4 instances of the sprite at the corners of the pointer
519 		for (int i = 0; i < 4; ++i)
520 		{
521 			const double angle = angleBase + i * 90;
522 			const double x = screenpos[0] + size * cos(angle / 180 * M_PI);
523 			const double y = screenpos[1] + size * sin(angle / 180 * M_PI);
524 			sPainter.drawSprite2dMode(x, y, 10, angle);
525 		}
526 	}
527 }
528 
keplerOrbitPosFunc(double jd,double xyz[3],double xyzdot[3],void * orbitPtr)529 void keplerOrbitPosFunc(double jd,double xyz[3], double xyzdot[3], void* orbitPtr)
530 {
531 	static_cast<KeplerOrbit*>(orbitPtr)->positionAtTimevInVSOP87Coordinates(jd, xyz);
532 	static_cast<KeplerOrbit*>(orbitPtr)->getVelocity(xyzdot);
533 }
534 
gimbalOrbitPosFunc(double jd,double xyz[3],double xyzdot[3],void * orbitPtr)535 void gimbalOrbitPosFunc(double jd,double xyz[3], double xyzdot[3], void* orbitPtr)
536 {
537 	static_cast<GimbalOrbit*>(orbitPtr)->positionAtTimevInVSOP87Coordinates(jd, xyz);
538 	static_cast<GimbalOrbit*>(orbitPtr)->getVelocity(xyzdot);
539 }
540 
541 // Init and load the solar system data (2 files)
loadPlanets()542 void SolarSystem::loadPlanets()
543 {
544 	minorBodies.clear();
545 	systemMinorBodies.clear();
546 	qDebug() << "Loading Solar System data (1: planets and moons) ...";
547 	QString solarSystemFile = StelFileMgr::findFile("data/ssystem_major.ini");
548 	if (solarSystemFile.isEmpty())
549 	{
550 		qWarning() << "ERROR while loading ssystem_major.ini (unable to find data/ssystem_major.ini): " << StelUtils::getEndLineChar();
551 		return;
552 	}
553 
554 	if (!loadPlanets(solarSystemFile))
555 	{
556 		qWarning() << "ERROR while loading ssystem_major.ini: " << StelUtils::getEndLineChar();
557 		return;
558 	}
559 
560 	qDebug() << "Loading Solar System data (2: minor bodies)...";
561 	QStringList solarSystemFiles = StelFileMgr::findFileInAllPaths("data/ssystem_minor.ini");
562 	if (solarSystemFiles.isEmpty())
563 	{
564 		qWarning() << "ERROR while loading ssystem_minor.ini (unable to find data/ssystem_minor.ini): " << StelUtils::getEndLineChar();
565 		return;
566 	}
567 
568 	for (const auto& solarSystemFile : qAsConst(solarSystemFiles))
569 	{
570 		if (loadPlanets(solarSystemFile))
571 		{
572 			qDebug() << "File ssystem_minor.ini is loaded successfully...";
573 			break;
574 		}
575 		else
576 		{
577 //			sun.clear();
578 //			moon.clear();
579 //			earth.clear();
580 			//qCritical() << "We should not be here!";
581 
582 			qDebug() << "Removing minor bodies";
583 			for (const auto& p : systemPlanets)
584 			{
585 				// We can only delete minor objects now!
586 				if (p->pType >= Planet::isAsteroid)
587 				{
588 					p->satellites.clear();
589 				}
590 			}
591 			systemPlanets.clear();
592 			//Memory leak? What's the proper way of cleaning shared pointers?
593 
594 			// TODO: 0.16pre what about the orbits list?
595 
596 			//If the file is in the user data directory, rename it:
597 			if (solarSystemFile.contains(StelFileMgr::getUserDir()))
598 			{
599 				QString newName = QString("%1/data/ssystem-%2.ini").arg(StelFileMgr::getUserDir()).arg(QDateTime::currentDateTime().toString("yyyyMMddThhmmss"));
600 				if (QFile::rename(solarSystemFile, newName))
601 					qWarning() << "Invalid Solar System file" << QDir::toNativeSeparators(solarSystemFile) << "has been renamed to" << QDir::toNativeSeparators(newName);
602 				else
603 				{
604 					qWarning() << "Invalid Solar System file" << QDir::toNativeSeparators(solarSystemFile) << "cannot be removed!";
605 					qWarning() << "Please either delete it, rename it or move it elsewhere.";
606 				}
607 			}
608 		}
609 	}
610 
611 	shadowPlanetCount = 0;
612 
613 	for (const auto& planet : qAsConst(systemPlanets))
614 		if(planet->parent != sun || !planet->satellites.isEmpty())
615 			shadowPlanetCount++;
616 }
617 
BvToColorIndex(double bV)618 unsigned char SolarSystem::BvToColorIndex(double bV)
619 {
620 	const double dBV = qBound(-500., static_cast<double>(bV)*1000.0, 3499.);
621 	return static_cast<unsigned char>(floor(0.5+127.0*((500.0+dBV)/4000.0)));
622 }
623 
loadPlanets(const QString & filePath)624 bool SolarSystem::loadPlanets(const QString& filePath)
625 {
626 	StelSkyDrawer* skyDrawer = StelApp::getInstance().getCore()->getSkyDrawer();
627 	qDebug() << "Loading from :"  << filePath;
628 	QSettings pd(filePath, StelIniFormat);
629 	if (pd.status() != QSettings::NoError)
630 	{
631 		qWarning() << "ERROR while parsing" << QDir::toNativeSeparators(filePath);
632 		return false;
633 	}
634 
635 	// QSettings does not allow us to say that the sections of the file
636 	// will be listed in the same order  as in the file like the old
637 	// InitParser used to so we can no longer assume that.
638 	//
639 	// This means we must first decide what order to read the sections
640 	// of the file in (each section contains one planet/moon/asteroid/comet/...) to avoid setting
641 	// the parent Planet* to one which has not yet been created.
642 	//
643 	// Stage 1: Make a map of body names back to the section names
644 	// which they come from. Also make a map of body name to parent body
645 	// name. These two maps can be made in a single pass through the
646 	// sections of the file.
647 	//
648 	// Stage 2: Make an ordered list of section names such that each
649 	// item is only ever dependent on items which appear earlier in the
650 	// list.
651 	// 2a: Make a QMultiMap relating the number of levels of dependency
652 	//     to the body name, i.e.
653 	//     0 -> Sun
654 	//     1 -> Mercury
655 	//     1 -> Venus
656 	//     1 -> Earth
657 	//     2 -> Moon
658 	//     etc.
659 	// 2b: Populate an ordered list of section names by iterating over
660 	//     the QMultiMap.  This type of container is always sorted on the
661 	//     key in ascending order, so it's easy.
662 	//     i.e. [sun, earth, moon] is fine, but not [sun, moon, earth]
663 	//
664 	// Stage 3: iterate over the ordered sections decided in stage 2,
665 	// creating the planet objects from the QSettings data.
666 
667 	// Stage 1 (as described above).
668 	QMap<QString, QString> secNameMap;
669 	QMap<QString, QString> parentMap;
670 	QStringList sections = pd.childGroups();
671 	// qDebug() << "Stage 1: load ini file with" << sections.size() << "entries: "<< sections;
672 	for (int i=0; i<sections.size(); ++i)
673 	{
674 		const QString secname = sections.at(i);
675 		const QString englishName = pd.value(secname+"/name").toString();
676 		const QString strParent = pd.value(secname+"/parent", "Sun").toString();
677 		secNameMap[englishName] = secname;
678 		if (strParent!="none" && !strParent.isEmpty() && !englishName.isEmpty())
679 		{
680 			parentMap[englishName] = strParent;
681 			// qDebug() << "parentmap[" << englishName << "] = " << strParent;
682 		}
683 	}
684 
685 	// Stage 2a (as described above).
686 	QMultiMap<int, QString> depLevelMap;
687 	for (int i=0; i<sections.size(); ++i)
688 	{
689 		const QString englishName = pd.value(sections.at(i)+"/name").toString();
690 
691 		// follow dependencies, incrementing level when we have one
692 		// till we run out.
693 		QString p=englishName;
694 		int level = 0;
695 		while(parentMap.contains(p) && parentMap[p]!="none")
696 		{
697 			level++;
698 			p = parentMap[p];
699 		}
700 
701 		depLevelMap.insert(level, secNameMap[englishName]);
702 		// qDebug() << "2a: Level" << level << "secNameMap[" << englishName << "]="<< secNameMap[englishName];
703 	}
704 
705 	// Stage 2b (as described above).
706 	// qDebug() << "Stage 2b:";
707 	QStringList orderedSections;
708 	QMapIterator<int, QString> levelMapIt(depLevelMap);
709 	while(levelMapIt.hasNext())
710 	{
711 		levelMapIt.next();
712 		orderedSections << levelMapIt.value();
713 	}
714 	// qDebug() << orderedSections;
715 
716 	// Stage 3 (as described above).
717 	int readOk=0;
718 	//int totalPlanets=0;
719 
720 	// qDebug() << "Adding " << orderedSections.size() << "objects...";
721 	for (int i = 0;i<orderedSections.size();++i)
722 	{
723 		// qDebug() << "Processing entry" << orderedSections.at(i);
724 
725 		//totalPlanets++;
726 		const QString secname = orderedSections.at(i);
727 		const QString englishName = pd.value(secname+"/name").toString().simplified();
728 		const QString strParent = pd.value(secname+"/parent", "Sun").toString(); // Obvious default, keep file entries simple.
729 		PlanetP parent;
730 		if (strParent!="none")
731 		{
732 			// Look in the other planets the one named with strParent
733 			for (const auto& p : qAsConst(systemPlanets))
734 			{
735 				if (p->getEnglishName()==strParent)
736 				{
737 					parent = p;
738 					break;
739 				}
740 			}
741 			if (parent.isNull())
742 			{
743 				qWarning() << "ERROR : can't find parent solar system body for " << englishName << ". Skipping.";
744 				//abort();
745 				continue;
746 			}
747 		}
748 		Q_ASSERT(parent || englishName=="Sun");
749 
750 		const QString coordFuncName = pd.value(secname+"/coord_func", "kepler_orbit").toString(); // 0.20: new default for all non *_special.
751 		// qDebug() << "englishName:" << englishName << ", parent:" << strParent <<  ", coord_func:" << coordFuncName;
752 		posFuncType posfunc=Q_NULLPTR;
753 		Orbit* orbitPtr=Q_NULLPTR;
754 		OsculatingFunctType *osculatingFunc = Q_NULLPTR;
755 		bool closeOrbit = true;
756 		double semi_major_axis=0; // used again below.
757 		const QString type = pd.value(secname+"/type").toString();
758 
759 
760 #ifdef USE_GIMBAL_ORBIT
761 		// undefine the flag in Orbit.h to disable and use the old, static observer solution (on an infinitely slow KeplerOrbit)
762 		// Note that for now we ignore any orbit-related config values from the ini file.
763 		if (type=="observer")
764 		{
765 			// Create a pseudo orbit that allows interaction with keyboard
766 			GimbalOrbit *orb = new GimbalOrbit(1, 0., 90.);    // [1 AU over north pole]
767 			orbits.push_back(orb);
768 
769 			orbitPtr = orb;
770 			posfunc = &gimbalOrbitPosFunc;
771 		}
772 		else
773 #endif
774 		if ((coordFuncName=="kepler_orbit") || (coordFuncName=="comet_orbit") || (coordFuncName=="ell_orbit")) // ell_orbit used for planet moons. TBD in V1.0: remove non-kepler_orbit!
775 		{
776 			// ell_orbit was used for planet moons, comet_orbit for minor bodies. The only difference is that pericenter distance for moons is given in km, not AU.
777 			// Read the orbital elements
778 			const double eccentricity = pd.value(secname+"/orbit_Eccentricity", 0.0).toDouble();
779 			if (eccentricity >= 1.0) closeOrbit = false;
780 			double pericenterDistance = pd.value(secname+"/orbit_PericenterDistance",-1e100).toDouble(); // AU, or km for ell_orbit!
781 			if (pericenterDistance <= 0.0) {
782 				semi_major_axis = pd.value(secname+"/orbit_SemiMajorAxis",-1e100).toDouble();
783 				if (semi_major_axis <= -1e100) {
784 					qDebug() << "ERROR loading " << englishName
785 						 << ": you must provide orbit_PericenterDistance or orbit_SemiMajorAxis. Skipping " << englishName;
786 					continue;
787 				} else {
788 					Q_ASSERT(eccentricity != 1.0); // parabolic orbits have no semi_major_axis
789 					pericenterDistance = semi_major_axis * (1.0-eccentricity);
790 				}
791 			} else {
792 				semi_major_axis = (eccentricity == 1.0)
793 								? 0.0 // parabolic orbits have no semi_major_axis
794 								: pericenterDistance / (1.0-eccentricity);
795 			}
796 			if (strParent!="Sun")
797 				pericenterDistance /= AU;  // Planet moons have distances given in km in the .ini file! But all further computation done in AU.
798 
799 			double meanMotion = pd.value(secname+"/orbit_MeanMotion",-1e100).toDouble(); // degrees/day
800 			if (meanMotion <= -1e100) {
801 				const double period = pd.value(secname+"/orbit_Period",-1e100).toDouble();
802 				if (period <= -1e100) {
803 					if (parent->getParent()) {
804 						qWarning() << "ERROR: " << englishName
805 							   << ": when the parent body is not the sun, you must provide "
806 							   << "either orbit_MeanMotion or orbit_Period";
807 					} else {
808 						// in case of parent=sun: use Gaussian gravitational constant for calculating meanMotion:
809 						meanMotion = (eccentricity == 1.0)
810 									? 0.01720209895 * (1.5/pericenterDistance) * std::sqrt(0.5/pericenterDistance)  // Heafner: Fund.Eph.Comp. W / dt
811 									: 0.01720209895 / (fabs(semi_major_axis)*std::sqrt(fabs(semi_major_axis)));
812 					}
813 				} else {
814 					meanMotion = 2.0*M_PI/period;
815 				}
816 			} else {
817 				meanMotion *= (M_PI/180.0);
818 			}
819 
820 			const double ascending_node = pd.value(secname+"/orbit_AscendingNode", 0.0).toDouble()*(M_PI/180.0);
821 			double arg_of_pericenter = pd.value(secname+"/orbit_ArgOfPericenter",-1e100).toDouble();
822 			double long_of_pericenter;
823 			if (arg_of_pericenter <= -1e100) {
824 				long_of_pericenter = pd.value(secname+"/orbit_LongOfPericenter", 0.0).toDouble()*(M_PI/180.0);
825 				arg_of_pericenter = long_of_pericenter - ascending_node;
826 			} else {
827 				arg_of_pericenter *= (M_PI/180.0);
828 				long_of_pericenter = arg_of_pericenter + ascending_node;
829 			}
830 
831 			double time_at_pericenter = pd.value(secname+"/orbit_TimeAtPericenter",-1e100).toDouble();
832 			// In earlier times (up to 0.21.2) we did not care much to store orbital epoch for comets but silently assumed T for it in various places.
833 			// However, the distinction is relevant to discern element sets for various valid ranges.
834 			// Comet orbits epoch should default to T while planets or moons default to J2000.
835 			const double epoch = pd.value(secname+"/orbit_Epoch", type=="comet" ? time_at_pericenter : J2000).toDouble();
836 			if (time_at_pericenter <= -1e100) {
837 				double mean_anomaly = pd.value(secname+"/orbit_MeanAnomaly",-1e100).toDouble()*(M_PI/180.0);
838 				if (mean_anomaly <= -1e10) {
839 					double mean_longitude = pd.value(secname+"/orbit_MeanLongitude",-1e100).toDouble()*(M_PI/180.0);
840 					if (mean_longitude <= -1e10) {
841 						qWarning() << "ERROR: " << englishName
842 							   << ": when you do not provide orbit_TimeAtPericenter, you must provide orbit_Epoch"
843 							   << "and either one of orbit_MeanAnomaly or orbit_MeanLongitude. Skipping this object.";
844 						//abort();
845 						continue;
846 					} else {
847 						mean_anomaly = mean_longitude - long_of_pericenter;
848 					}
849 				}
850 				time_at_pericenter = epoch - mean_anomaly / meanMotion;
851 			}
852 
853 			static const QMap<QString, double>massMap={ // masses from DE430/431
854 				{ "Sun",            1.0},
855 				{ "Mercury",  6023682.155592},
856 				{ "Venus",     408523.718658},
857 				{ "Earth",     332946.048834},
858 				{ "Mars",     3098703.590291},
859 				{ "Jupiter",     1047.348625},
860 				{ "Saturn",      3497.901768},
861 				{ "Uranus",     22902.981613},
862 				{ "Neptune",    19412.259776},
863 				{ "Pluto",  135836683.768617}};
864 
865 			// Construct orbital elements relative to the parent body. This will construct orbits for J2000 only.
866 			// Some planet axes move very slowly, this effect could be modelled by replicating these lines
867 			// after recomputing obliquity and node (below) in Planet::computeTransMatrix().
868 			// The effect is negligible for several millennia, though.
869 			// When the parent is the sun use ecliptic rather than sun equator:
870 			const double parentRotObliquity  = parent->getParent() ? parent->getRotObliquity(J2000) : 0.0;
871 			const double parent_rot_asc_node = parent->getParent() ? parent->getRotAscendingNode()  : 0.0;
872 			double parent_rot_j2000_longitude = 0.0;
873 			if (parent->getParent()) {
874 				const double c_obl = cos(parentRotObliquity);
875 				const double s_obl = sin(parentRotObliquity);
876 				const double c_nod = cos(parent_rot_asc_node);
877 				const double s_nod = sin(parent_rot_asc_node);
878 				const Vec3d OrbitAxis0( c_nod,       s_nod,        0.0);
879 				const Vec3d OrbitAxis1(-s_nod*c_obl, c_nod*c_obl,s_obl);
880 				const Vec3d OrbitPole(  s_nod*s_obl,-c_nod*s_obl,c_obl);
881 				const Vec3d J2000Pole(StelCore::matJ2000ToVsop87.multiplyWithoutTranslation(Vec3d(0,0,1)));
882 				Vec3d J2000NodeOrigin(J2000Pole^OrbitPole);
883 				J2000NodeOrigin.normalize();
884 				parent_rot_j2000_longitude = atan2(J2000NodeOrigin*OrbitAxis1,J2000NodeOrigin*OrbitAxis0);
885 			}
886 
887 			const double orbitGoodDays=pd.value(secname+"/orbit_good", parent->englishName!="Sun" ? 0. : -1.).toDouble(); // "Moons" have permanently good orbits.
888 			const double inclination = pd.value(secname+"/orbit_Inclination", 0.0).toDouble()*(M_PI/180.0);
889 
890 			// Create a Keplerian orbit. This has been called CometOrbit before 0.20.
891 			//qDebug() << "Creating KeplerOrbit for" << parent->englishName << "---" << englishName;
892 			KeplerOrbit *orb = new KeplerOrbit(epoch,                  // JDE
893 							   pericenterDistance,     // [AU]
894 							   eccentricity,           // 0..>1 (>>1 for Interstellar objects)
895 							   inclination,            // [radians]
896 							   ascending_node,         // [radians]
897 							   arg_of_pericenter,      // [radians]
898 							   time_at_pericenter,     // JDE
899 							   orbitGoodDays,          // orbitGoodDays. 0=always good, -1=compute_half_orbit_duration
900 							   meanMotion,             // [radians/day]
901 							   parentRotObliquity,     // [radians]
902 							   parent_rot_asc_node,    // [radians]
903 							   parent_rot_j2000_longitude, // [radians]
904 							   1./massMap.value(parent->englishName, 1.)); // central mass [solar masses]
905 			orbits.push_back(orb);
906 
907 			orbitPtr = orb;
908 			posfunc = &keplerOrbitPosFunc;
909 		}
910 		else
911 		{
912 			static const QMap<QString, posFuncType>posfuncMap={
913 				{ "sun_special",       &get_sun_helio_coordsv},
914 				{ "mercury_special",   &get_mercury_helio_coordsv},
915 				{ "venus_special",     &get_venus_helio_coordsv},
916 				{ "earth_special",     &get_earth_helio_coordsv},
917 				{ "lunar_special",     &get_lunar_parent_coordsv},
918 				{ "mars_special",      &get_mars_helio_coordsv},
919 				{ "phobos_special",    &get_phobos_parent_coordsv},
920 				{ "deimos_special",    &get_deimos_parent_coordsv},
921 				{ "jupiter_special",   &get_jupiter_helio_coordsv},
922 				{ "io_special",        &get_io_parent_coordsv},
923 				{ "europa_special",    &get_europa_parent_coordsv},
924 				{ "ganymede_special",  &get_ganymede_parent_coordsv},
925 				{ "calisto_special",   &get_callisto_parent_coordsv},
926 				{ "callisto_special",  &get_callisto_parent_coordsv},
927 				{ "saturn_special",    &get_saturn_helio_coordsv},
928 				{ "mimas_special",     &get_mimas_parent_coordsv},
929 				{ "enceladus_special", &get_enceladus_parent_coordsv},
930 				{ "tethys_special",    &get_tethys_parent_coordsv},
931 				{ "dione_special",     &get_dione_parent_coordsv},
932 				{ "rhea_special",      &get_rhea_parent_coordsv},
933 				{ "titan_special",     &get_titan_parent_coordsv},
934 				{ "hyperion_special",  &get_hyperion_parent_coordsv},
935 				{ "iapetus_special",   &get_iapetus_parent_coordsv},
936 				{ "helene_special",    &get_helene_parent_coordsv},
937 				{ "telesto_special",   &get_telesto_parent_coordsv},
938 				{ "calypso_special",   &get_calypso_parent_coordsv},
939 				{ "uranus_special",    &get_uranus_helio_coordsv},
940 				{ "miranda_special",   &get_miranda_parent_coordsv},
941 				{ "ariel_special",     &get_ariel_parent_coordsv},
942 				{ "umbriel_special",   &get_umbriel_parent_coordsv},
943 				{ "titania_special",   &get_titania_parent_coordsv},
944 				{ "oberon_special",    &get_oberon_parent_coordsv},
945 				{ "neptune_special",   &get_neptune_helio_coordsv},
946 				{ "pluto_special",     &get_pluto_helio_coordsv}};
947 			static const QMap<QString, OsculatingFunctType*>osculatingMap={
948 				{ "mercury_special",   &get_mercury_helio_osculating_coords},
949 				{ "venus_special",     &get_venus_helio_osculating_coords},
950 				{ "earth_special",     &get_earth_helio_osculating_coords},
951 				{ "mars_special",      &get_mars_helio_osculating_coords},
952 				{ "jupiter_special",   &get_jupiter_helio_osculating_coords},
953 				{ "saturn_special",    &get_saturn_helio_osculating_coords},
954 				{ "uranus_special",    &get_uranus_helio_osculating_coords},
955 				{ "neptune_special",   &get_neptune_helio_osculating_coords}};
956 			posfunc=posfuncMap.value(coordFuncName, Q_NULLPTR);
957 			osculatingFunc=osculatingMap.value(coordFuncName, Q_NULLPTR);
958 		}
959 		if (posfunc==Q_NULLPTR)
960 		{
961 			qCritical() << "ERROR in section " << secname << ": can't find posfunc " << coordFuncName << " for " << englishName;
962 			exit(-1);
963 		}
964 
965 		// Create the Solar System body and add it to the list
966 		//TODO: Refactor the subclass selection to reduce duplicate code mess here,
967 		// by at least using this base class pointer and using setXXX functions instead of mega-constructors
968 		// that have to pass most of it on to the Planet class
969 		PlanetP newP;
970 
971 		// New class objects, named "plutino", "cubewano", "dwarf planet", "SDO", "OCO", has properties
972 		// similar to asteroids and we should calculate their positions like for asteroids. Dwarf planets
973 		// have one exception: Pluto - as long as we use a special function for calculation of Pluto's orbit.
974 		if ((type == "asteroid" || type == "dwarf planet" || type == "cubewano" || type=="sednoid" || type == "plutino" || type == "scattered disc object" || type == "Oort cloud object" || type == "interstellar object") && !englishName.contains("Pluto"))
975 		{
976 			minorBodies << englishName;
977 
978 			Vec3f color = Vec3f(1.f, 1.f, 1.f);
979 			const float bV = pd.value(secname+"/color_index_bv", 99.f).toFloat();
980 			if (bV<99.f)
981 				color = skyDrawer->indexToColor(BvToColorIndex(bV))*0.75f; // see ZoneArray.cpp:L490
982 			else
983 				color = Vec3f(pd.value(secname+"/color", "1.0,1.0,1.0").toString());
984 
985 			const bool hidden = pd.value(secname+"/hidden", false).toBool();
986 			const QString normalMapName = ( hidden ? "" : englishName.toLower().append("_normals.png")); // no normal maps for invisible objects!
987 
988 			newP = PlanetP(new MinorPlanet(englishName,
989 						    pd.value(secname+"/radius", 1.0).toDouble()/AU,
990 						    pd.value(secname+"/oblateness", 0.0).toDouble(),
991 						    color, // halo color
992 						    pd.value(secname+"/albedo", 0.25f).toFloat(),
993 						    pd.value(secname+"/roughness",0.9f).toFloat(),
994 						    pd.value(secname+"/tex_map", "nomap.png").toString(),
995 						    pd.value(secname+"/normals_map", normalMapName).toString(),
996 						    pd.value(secname+"/model").toString(),
997 						    posfunc,
998 						    static_cast<KeplerOrbit*>(orbitPtr), // the KeplerOrbit object created previously
999 						    osculatingFunc, // should be Q_NULLPTR
1000 						    closeOrbit,
1001 						    hidden,
1002 						    type));
1003 			QSharedPointer<MinorPlanet> mp =  newP.dynamicCast<MinorPlanet>();
1004 			//Number, Provisional designation
1005 			mp->setMinorPlanetNumber(pd.value(secname+"/minor_planet_number", 0).toInt());
1006 			mp->setProvisionalDesignation(pd.value(secname+"/provisional_designation", "").toString());
1007 
1008 			//H-G magnitude system
1009 			const float magnitude = pd.value(secname+"/absolute_magnitude", -99.f).toFloat();
1010 			const float slope = pd.value(secname+"/slope_parameter", 0.15f).toFloat();
1011 			if (magnitude > -99.f)
1012 			{
1013 				mp->setAbsoluteMagnitudeAndSlope(magnitude, qBound(0.0f, slope, 1.0f));
1014 			}
1015 
1016 			mp->setColorIndexBV(bV);
1017 			mp->setSpectralType(pd.value(secname+"/spec_t", "").toString(), pd.value(secname+"/spec_b", "").toString());
1018 			if (semi_major_axis>0)
1019 				mp->deltaJDE = 2.0*semi_major_axis*StelCore::JD_SECOND;
1020 			 else if ((semi_major_axis<=0.0) && (type!="interstellar object"))
1021 				qWarning() << "WARNING: Minor Body" << englishName << "has no semimajor axis!";
1022 
1023 			systemMinorBodies.push_back(newP);
1024 		}
1025 		else if (type == "comet")
1026 		{
1027 			minorBodies << englishName;
1028 			newP = PlanetP(new Comet(englishName,
1029 					      pd.value(secname+"/radius", 1.0).toDouble()/AU,
1030 					      pd.value(secname+"/oblateness", 0.0).toDouble(),
1031 					      Vec3f(pd.value(secname+"/color", "1.0,1.0,1.0").toString()), // halo color
1032 					      pd.value(secname+"/albedo", 0.075f).toFloat(), // assume very dark surface
1033 					      pd.value(secname+"/roughness",0.9f).toFloat(),
1034 					      pd.value(secname+"/outgas_intensity",0.1f).toFloat(),
1035 					      pd.value(secname+"/outgas_falloff", 0.1f).toFloat(),
1036 					      pd.value(secname+"/tex_map", "nomap.png").toString(),
1037 					      pd.value(secname+"/model").toString(),
1038 					      posfunc,
1039 					      static_cast<KeplerOrbit*>(orbitPtr), // the KeplerOrbit object
1040 					      osculatingFunc, // ALWAYS NULL for comets.
1041 					      closeOrbit,
1042 					      pd.value(secname+"/hidden", false).toBool(),
1043 					      type,
1044 					      pd.value(secname+"/dust_widthfactor", 1.5f).toFloat(),
1045 					      pd.value(secname+"/dust_lengthfactor", 0.4f).toFloat(),
1046 					      pd.value(secname+"/dust_brightnessfactor", 1.5f).toFloat()
1047 					      ));
1048 			QSharedPointer<Comet> mp = newP.dynamicCast<Comet>();
1049 
1050 			//g,k magnitude system
1051 			const float magnitude = pd.value(secname+"/absolute_magnitude", -99).toFloat();
1052 			const float slope = qBound(-5.0f, pd.value(secname+"/slope_parameter", 4.0f).toFloat(), 30.0f);
1053 			if (magnitude > -99)
1054 			{
1055 					mp->setAbsoluteMagnitudeAndSlope(magnitude, slope);
1056 			}
1057 
1058 			systemMinorBodies.push_back(newP);
1059 		}
1060 		else // type==star|planet|moon|dwarf planet|observer|artificial
1061 		{
1062 			//qDebug() << type;
1063 			Q_ASSERT(type=="star" || type=="planet" || type=="moon" || type=="artificial" || type=="observer" || type=="dwarf planet"); // TBD: remove Pluto...
1064 			// Set possible default name of the normal map for avoiding yin-yang shaped moon
1065 			// phase when normal map key not exists. Example: moon_normals.png
1066 			// Details: https://bugs.launchpad.net/stellarium/+bug/1335609
1067 			newP = PlanetP(new Planet(englishName,
1068 					       pd.value(secname+"/radius", 1.0).toDouble()/AU,
1069 					       pd.value(secname+"/oblateness", 0.0).toDouble(),
1070 					       Vec3f(pd.value(secname+"/color", "1.0,1.0,1.0").toString()), // halo color
1071 					       pd.value(secname+"/albedo", 0.25f).toFloat(),
1072 					       pd.value(secname+"/roughness",0.9f).toFloat(),
1073 					       pd.value(secname+"/tex_map", "nomap.png").toString(),
1074 					       pd.value(secname+"/normals_map", englishName.toLower().append("_normals.png")).toString(),
1075 					       pd.value(secname+"/model").toString(),
1076 					       posfunc,
1077 					       static_cast<KeplerOrbit*>(orbitPtr), // This remains Q_NULLPTR for the major planets, or has a KeplerOrbit for planet moons.
1078 					       osculatingFunc,
1079 					       closeOrbit,
1080 					       pd.value(secname+"/hidden", false).toBool(),
1081 					       pd.value(secname+"/atmosphere", false).toBool(),
1082 					       pd.value(secname+"/halo", true).toBool(),
1083 					       type));
1084 			newP->absoluteMagnitude = pd.value(secname+"/absolute_magnitude", -99.f).toFloat();
1085 
1086 			// Moon designation (planet index + IAU moon number)
1087 			QString moonDesignation = pd.value(secname+"/iau_moon_number", "").toString();
1088 			if (!moonDesignation.isEmpty())
1089 			{
1090 				newP->setIAUMoonNumber(moonDesignation);
1091 			}
1092 		}
1093 
1094 		if (!parent.isNull())
1095 		{
1096 			parent->satellites.append(newP);
1097 			newP->parent = parent;
1098 		}
1099 		if (secname=="earth") earth = newP;
1100 		if (secname=="sun") sun = newP;
1101 		if (secname=="moon") moon = newP;
1102 
1103 		// At this point the orbit and object type (class Planet and subclasses) have been fixed.
1104 		// For many objects we have oriented spheroids with rotational parameters.
1105 
1106 		// There are two ways of defining the axis orientation:
1107 		// obliquity and ascending node, which was used by Stellarium already before 2010 (based on Celestia?).
1108 		double rotObliquity = pd.value(secname+"/rot_obliquity",0.).toDouble()*(M_PI_180);
1109 		double rotAscNode = pd.value(secname+"/rot_equator_ascending_node",0.).toDouble()*(M_PI_180);
1110 		// rot_periode given in hours (from which rotPeriod in days),
1111 		// The default is useful for many moons in bound rotation
1112 		double rotPeriod=pd.value(secname+"/rot_periode", pd.value(secname+"/orbit_Period", 1.).toDouble()*24.).toDouble()/24.;
1113 		double rotOffset=pd.value(secname+"/rot_rotation_offset",0.).toDouble();
1114 
1115 		// 0.21+: Use WGCCRE planet North pole data if available
1116 		// NB: N pole for J2000 epoch as defined by IAU (NOT right hand rotation rule)
1117 		// Define only basic motion. Use special functions for more complicated axes.
1118 		const double J2000NPoleRA  = pd.value(secname+"/rot_pole_ra",  0.).toDouble()*M_PI_180;
1119 		const double J2000NPoleRA1 = pd.value(secname+"/rot_pole_ra1", 0.).toDouble()*M_PI_180;
1120 		const double J2000NPoleDE  = pd.value(secname+"/rot_pole_de",  0.).toDouble()*M_PI_180;
1121 		const double J2000NPoleDE1 = pd.value(secname+"/rot_pole_de1", 0.).toDouble()*M_PI_180;
1122 		const double J2000NPoleW0  = pd.value(secname+"/rot_pole_w0",  0.).toDouble(); // [degrees]   Basically the same idea as rot_rotation_offset, but W!=rotAngle
1123 		const double J2000NPoleW1  = pd.value(secname+"/rot_pole_w1",  0.).toDouble(); // [degrees/d] Basically the same idea as 360/rot_periode
1124 		if (fabs(J2000NPoleW1) > 0.0) // Patch possibly old period value with a more modern value.
1125 		{
1126 			// this is just another expression for rotational speed.
1127 			rotPeriod=360.0/J2000NPoleW1;
1128 		}
1129 
1130 		// IMPORTANT: For the planet moons with orbits relative to planets' equator plane,
1131 		// re-compute the important bits from the updated axis elements.
1132 		// Reactivated to re-establish Pluto/Charon lock #153
1133 		if((J2000NPoleRA!=0.) || (J2000NPoleDE!=0.))
1134 		{
1135 			// If available, recompute obliquity and AscNode from the new data.
1136 			// Solution since 0.16: Make this once for J2000.
1137 			// Optional (future?): Repeat this block in Planet::computeTransMatrix() for planets with moving axes and update all Moons' KeplerOrbit if required.
1138 			Vec3d J2000NPole;
1139 			StelUtils::spheToRect(J2000NPoleRA,J2000NPoleDE,J2000NPole);
1140 
1141 			Vec3d vsop87Pole(StelCore::matJ2000ToVsop87.multiplyWithoutTranslation(J2000NPole));
1142 
1143 			double lon, lat;
1144 			StelUtils::rectToSphe(&lon, &lat, vsop87Pole);
1145 
1146 			rotObliquity = (M_PI_2 - lat);
1147 			rotAscNode = (lon + M_PI_2);
1148 
1149 			//qDebug() << englishName << ": Compare these values to the older data in ssystem_major";
1150 			//qDebug() << "\tCalculated rotational obliquity: " << rotObliquity*180./M_PI;
1151 			//qDebug() << "\tCalculated rotational ascending node: " << rotAscNode*180./M_PI;
1152 
1153 			if (J2000NPoleW0 >0)
1154 			{
1155 				// W0 is counted from the ascending node with ICRF, but rotOffset from orbital plane.
1156 				// Try this assumption by just counting Offset=W0+90+RA0.
1157 				rotOffset=J2000NPoleW0 + lon*M_180_PI;
1158 				//qDebug() << "\tCalculated rotational period (days // hours): " << rotPeriod << "//" << rotPeriod*24.;
1159 				//qDebug() << "\tRotational offset (degrees): " << rotOffset;
1160 			}
1161 		}
1162 		newP->setRotationElements(
1163 			englishName,
1164 			rotPeriod,
1165 			rotOffset,
1166 			pd.value(secname+"/rot_epoch", J2000).toDouble(),
1167 			rotObliquity,
1168 			rotAscNode,
1169 			J2000NPoleRA,
1170 			J2000NPoleRA1,
1171 			J2000NPoleDE,
1172 			J2000NPoleDE1,
1173 			J2000NPoleW0,
1174 			J2000NPoleW1);
1175 		// orbit_Period or orbit_visualization_period given in days.
1176 		// Elliptical Kepler orbits (ecc<0.9) will replace whatever is given by a value computed on the fly.
1177 		newP->setSiderealPeriod(fabs(pd.value(secname+"/orbit_Period",
1178 						      pd.value(secname+"/orbit_visualization_period" )).toDouble()));
1179 
1180 		if (pd.contains(secname+"/tex_ring")) {
1181 			const float rMin = pd.value(secname+"/ring_inner_size").toFloat()/AUf;
1182 			const float rMax = pd.value(secname+"/ring_outer_size").toFloat()/AUf;
1183 			Ring *r = new Ring(rMin,rMax,pd.value(secname+"/tex_ring").toString());
1184 			newP->setRings(r);
1185 		}
1186 
1187 		systemPlanets.push_back(newP);
1188 		readOk++;
1189 	}
1190 
1191 	if (systemPlanets.isEmpty())
1192 	{
1193 		qWarning() << "No Solar System objects loaded from" << QDir::toNativeSeparators(filePath);
1194 		return false;
1195 	}
1196 	else qDebug() << "SolarSystem has " << systemPlanets.count() << "entries.";
1197 
1198 	// special case: load earth shadow texture
1199 	if (!Planet::texEarthShadow)
1200 		Planet::texEarthShadow = StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/earth-shadow.png");
1201 
1202 	// Also comets just have static textures.
1203 	if (!Comet::comaTexture)
1204 		Comet::comaTexture = StelApp::getInstance().getTextureManager().createTextureThread(StelFileMgr::getInstallationDir()+"/textures/cometComa.png", StelTexture::StelTextureParams(true, GL_LINEAR, GL_CLAMP_TO_EDGE));
1205 	//tail textures. We use paraboloid tail bodies, textured like a fisheye sphere, i.e. center=head. The texture should be something like a mottled star to give some structure.
1206 	if (!Comet::tailTexture)
1207 		Comet::tailTexture = StelApp::getInstance().getTextureManager().createTextureThread(StelFileMgr::getInstallationDir()+"/textures/cometTail.png", StelTexture::StelTextureParams(true, GL_LINEAR, GL_CLAMP_TO_EDGE));
1208 
1209 	if (readOk>0)
1210 		qDebug() << "Loaded" << readOk << "Solar System bodies";
1211 
1212 	return true;
1213 }
1214 
1215 // Compute the position for every elements of the solar system.
1216 // The order is not important since the position is computed relatively to the mother body
computePositions(double dateJDE,PlanetP observerPlanet)1217 void SolarSystem::computePositions(double dateJDE, PlanetP observerPlanet)
1218 {
1219 	StelCore *core=StelApp::getInstance().getCore();
1220 	const bool withAberration=core->getUseAberration();
1221 	if (flagLightTravelTime) // switching off light time correction implies no aberration for the planets.
1222 	{
1223 		for (const auto& p : qAsConst(systemPlanets))
1224 		{
1225 			p->computePosition(dateJDE, Vec3d(0.));
1226 		}
1227 		const Vec3d obsPosJDE=observerPlanet->getHeliocentricEclipticPos();
1228 
1229 		// For higher accuracy, we now make two iterations of light time and aberration correction. In the final round, we also compute rotation data.
1230 		// May fix sub-arcsecond inaccuracies, and optionally apply aberration in the way described in Explanatory Supplement (2013), 7.55.
1231 		// For reasons unknown (See discussion in GH:#1626) we do not add anything for the Moon when observed from Earth!
1232 		// Presumably the used ephemerides already provide aberration-corrected positions for the Moon?
1233 		const Vec3d aberrationPushSpeed=observerPlanet->getHeliocentricEclipticVelocity() * core->getAberrationFactor();
1234 		for (const auto& p : qAsConst(systemPlanets))
1235 		{
1236 			//p->setExtraInfoString(StelObject::DebugAid, "");
1237 			const double lightTimeDays = (p->getHeliocentricEclipticPos()-obsPosJDE).length() * (AU / (SPEED_OF_LIGHT * 86400.));
1238 			Vec3d aberrationPush(0.);
1239 			if (withAberration && (observerPlanet->englishName!="Earth" || p->englishName!="Moon"))
1240 				aberrationPush=lightTimeDays*aberrationPushSpeed;
1241 			p->computePosition(dateJDE-lightTimeDays, aberrationPush);
1242 		}
1243 		// Extra accuracy with another round. Not sure if useful. Maybe hide behind a new property flag?
1244 		for (const auto& p : qAsConst(systemPlanets))
1245 		{
1246 			//p->setExtraInfoString(StelObject::DebugAid, "");
1247 			const double lightTimeDays = (p->getHeliocentricEclipticPos()-obsPosJDE).length() * (AU / (SPEED_OF_LIGHT * 86400.));
1248 			Vec3d aberrationPush(0.);
1249 			if (withAberration && (observerPlanet->englishName!="Earth" || p->englishName!="Moon"))
1250 				aberrationPush=lightTimeDays*aberrationPushSpeed;
1251 			// The next call may already do nothing if the time difference to the previous round is not large enough.
1252 			p->computePosition(dateJDE-lightTimeDays, aberrationPush);
1253 //			p->setExtraInfoString(StelObject::DebugAid, QString("LightTime %1d; obsSpeed %2/%3/%4 AU/d")
1254 //					      .arg(QString::number(lightTimeDays, 'f', 3))
1255 //					      .arg(QString::number(aberrationPushSpeed[0], 'f', 3))
1256 //					      .arg(QString::number(aberrationPushSpeed[0], 'f', 3))
1257 //					      .arg(QString::number(aberrationPushSpeed[0], 'f', 3)));
1258 
1259 			if      (p->englishName=="Moon")    RotationElements::updatePlanetCorrections(dateJDE-lightTimeDays, RotationElements::EarthMoon);
1260 			else if (p->englishName=="Mars")    RotationElements::updatePlanetCorrections(dateJDE-lightTimeDays, RotationElements::Mars);
1261 			else if (p->englishName=="Jupiter") RotationElements::updatePlanetCorrections(dateJDE-lightTimeDays, RotationElements::Jupiter);
1262 			else if (p->englishName=="Saturn")  RotationElements::updatePlanetCorrections(dateJDE-lightTimeDays, RotationElements::Saturn);
1263 			else if (p->englishName=="Uranus")  RotationElements::updatePlanetCorrections(dateJDE-lightTimeDays, RotationElements::Uranus);
1264 			else if (p->englishName=="Neptune") RotationElements::updatePlanetCorrections(dateJDE-lightTimeDays, RotationElements::Neptune);
1265 		}
1266 	}
1267 	else
1268 	{
1269 		for (const auto& p : qAsConst(systemPlanets))
1270 		{
1271 			p->setExtraInfoString(StelObject::DebugAid, "");
1272 			p->computePosition(dateJDE, Vec3d(0.));
1273 			if      (p->englishName=="Moon")    RotationElements::updatePlanetCorrections(dateJDE, RotationElements::EarthMoon);
1274 			else if (p->englishName=="Mars")    RotationElements::updatePlanetCorrections(dateJDE, RotationElements::Mars);
1275 			else if (p->englishName=="Jupiter") RotationElements::updatePlanetCorrections(dateJDE, RotationElements::Jupiter);
1276 			else if (p->englishName=="Saturn")  RotationElements::updatePlanetCorrections(dateJDE, RotationElements::Saturn);
1277 			else if (p->englishName=="Uranus")  RotationElements::updatePlanetCorrections(dateJDE, RotationElements::Uranus);
1278 			else if (p->englishName=="Neptune") RotationElements::updatePlanetCorrections(dateJDE, RotationElements::Neptune);
1279 		}
1280 	}
1281 	computeTransMatrices(dateJDE, observerPlanet->getHeliocentricEclipticPos());
1282 }
1283 
1284 // Compute the transformation matrix for every elements of the solar system.
1285 // The elements have to be ordered hierarchically, eg. it's important to compute earth before moon.
computeTransMatrices(double dateJDE,const Vec3d & observerPos)1286 void SolarSystem::computeTransMatrices(double dateJDE, const Vec3d& observerPos)
1287 {
1288 	const double dateJD=dateJDE - (StelApp::getInstance().getCore()->computeDeltaT(dateJDE))/86400.0;
1289 
1290 	if (flagLightTravelTime)
1291 	{
1292 		for (const auto& p : qAsConst(systemPlanets))
1293 		{
1294 			const double light_speed_correction = (p->getHeliocentricEclipticPos()-observerPos).length() * (AU / (SPEED_OF_LIGHT * 86400));
1295 			p->computeTransMatrix(dateJD-light_speed_correction, dateJDE-light_speed_correction);
1296 		}
1297 	}
1298 	else
1299 	{
1300 		for (const auto& p : qAsConst(systemPlanets))
1301 		{
1302 			p->computeTransMatrix(dateJD, dateJDE);
1303 		}
1304 	}
1305 }
1306 
1307 // And sort them from the furthest to the closest to the observer
1308 struct biggerDistance : public std::binary_function<PlanetP, PlanetP, bool>
1309 {
operator ()biggerDistance1310 	bool operator()(PlanetP p1, PlanetP p2)
1311 	{
1312 		return p1->getDistance() > p2->getDistance();
1313 	}
1314 };
1315 
1316 // Draw all the elements of the solar system
1317 // We are supposed to be in heliocentric coordinate
draw(StelCore * core)1318 void SolarSystem::draw(StelCore* core)
1319 {
1320 	// AstroCalcDialog
1321 	drawEphemerisItems(core);
1322 
1323 	if (!flagShow)
1324 		return;
1325 
1326 	// Compute each Planet distance to the observer
1327 	const Vec3d obsHelioPos = core->getObserverHeliocentricEclipticPos();
1328 
1329 	for (const auto& p : qAsConst(systemPlanets))
1330 	{
1331 		p->computeDistance(obsHelioPos);
1332 	}
1333 
1334 	// And sort them from the furthest to the closest
1335 	sort(systemPlanets.begin(),systemPlanets.end(),biggerDistance());
1336 
1337 	if (trailFader.getInterstate()>0.0000001f)
1338 	{
1339 		StelPainter sPainter(core->getProjection2d());
1340 		const float ppx = static_cast<float>(sPainter.getProjector()->getDevicePixelsPerPixel());
1341 		allTrails->setOpacity(trailFader.getInterstate());
1342 		if (trailsThickness>1 || ppx>1.f)
1343 			sPainter.setLineWidth(trailsThickness*ppx);
1344 		allTrails->draw(core, &sPainter);
1345 		if (trailsThickness>1 || ppx>1.f)
1346 			sPainter.setLineWidth(1);
1347 	}
1348 
1349 	// Make some voodoo to determine when labels should be displayed
1350 	const float sdLimitMag=static_cast<float>(core->getSkyDrawer()->getLimitMagnitude());
1351 	const float maxMagLabel = (sdLimitMag<5.f ? sdLimitMag :
1352 			5.f+(sdLimitMag-5.f)*1.2f) +(static_cast<float>(labelsAmount)-3.f)*1.2f;
1353 
1354 	// Draw the elements
1355 	for (const auto& p : qAsConst(systemPlanets))
1356 	{
1357 		p->draw(core, maxMagLabel, planetNameFont);
1358 	}
1359 
1360 	if (GETSTELMODULE(StelObjectMgr)->getFlagSelectedObjectPointer() && getFlagPointer())
1361 		drawPointer(core);
1362 }
1363 
drawEphemerisItems(const StelCore * core)1364 void SolarSystem::drawEphemerisItems(const StelCore* core)
1365 {
1366 	if (flagShow || (!flagShow && getFlagEphemerisAlwaysOn()))
1367 	{
1368 		if (getFlagEphemerisMarkers())
1369 			drawEphemerisMarkers(core);
1370 		if (getFlagEphemerisLine())
1371 			drawEphemerisLine(core);
1372 	}
1373 }
1374 
getEphemerisMarkerColor(int index) const1375 Vec3f SolarSystem::getEphemerisMarkerColor(int index) const
1376 {
1377 	// Sync index with AstroCalcDialog::generateEphemeris(). If required, switch to using a QMap.
1378 	const QVector<Vec3f> colors={
1379 		ephemerisGenericMarkerColor,
1380 		ephemerisSecondaryMarkerColor,
1381 		ephemerisMercuryMarkerColor,
1382 		ephemerisVenusMarkerColor,
1383 		ephemerisMarsMarkerColor,
1384 		ephemerisJupiterMarkerColor,
1385 		ephemerisSaturnMarkerColor};
1386 	return colors.value(index, ephemerisGenericMarkerColor);
1387 }
1388 
drawEphemerisMarkers(const StelCore * core)1389 void SolarSystem::drawEphemerisMarkers(const StelCore *core)
1390 {
1391 	const int fsize = AstroCalcDialog::EphemerisList.count();
1392 	if (fsize==0) return;
1393 
1394 	StelProjectorP prj;
1395 	if (getFlagEphemerisHorizontalCoordinates())
1396 		prj = core->getProjection(StelCore::FrameAltAz, StelCore::RefractionOff);
1397 	else
1398 		prj = core->getProjection(StelCore::FrameJ2000);
1399 	StelPainter sPainter(prj);
1400 
1401 	float size, shift, baseSize = 4.f;
1402 	const bool showDates = getFlagEphemerisDates();
1403 	const bool showMagnitudes = getFlagEphemerisMagnitudes();
1404 	const bool showSkippedData = getFlagEphemerisSkipData();
1405 	const bool skipMarkers = getFlagEphemerisSkipMarkers();
1406 	const int dataStep = getEphemerisDataStep();
1407 	const int sizeCoeff = getEphemerisLineThickness() - 1;
1408 	QString info = "";
1409 	Vec3d win;
1410 	Vec3f markerColor;
1411 
1412 	if (getFlagEphemerisLine() && getFlagEphemerisScaleMarkers())
1413 		baseSize = 3.f; // The line lies through center of marker
1414 
1415 	for (int i =0; i < fsize; i++)
1416 	{
1417 		// Check visibility of pointer
1418 		if (!(sPainter.getProjector()->projectCheck(AstroCalcDialog::EphemerisList[i].coord, win)))
1419 			continue;
1420 
1421 		float solarAngle=0.f; // Angle to possibly rotate the texture. Degrees.
1422 		QString debugStr; // Used temporarily for development
1423 		const bool isComet=AstroCalcDialog::EphemerisList[i].isComet;
1424 		if (i == AstroCalcDialog::DisplayedPositionIndex)
1425 		{
1426 			markerColor = getEphemerisSelectedMarkerColor();
1427 			size = 6.f;
1428 		}
1429 		else
1430 		{
1431 			markerColor = getEphemerisMarkerColor(AstroCalcDialog::EphemerisList[i].colorIndex);
1432 			size = baseSize;
1433 		}
1434 		if (isComet) size += 16.f;
1435 		size += sizeCoeff; //
1436 		sPainter.setColor(markerColor);
1437 		sPainter.setBlending(true, GL_ONE, GL_ONE);
1438 		if (isComet)
1439 			texEphemerisCometMarker->bind();
1440 		else
1441 			texEphemerisMarker->bind();
1442 		if (skipMarkers)
1443 		{
1444 			if ((showDates || showMagnitudes) && showSkippedData && ((i + 1)%dataStep)!=1 && dataStep!=1)
1445 				continue;
1446 		}
1447 		Vec3d win;
1448 		if (prj->project(AstroCalcDialog::EphemerisList[i].coord, win))
1449 		{
1450 			if (isComet)
1451 			{
1452 				// compute solarAngle in screen space.
1453 				Vec3d sunWin;
1454 				prj->project(AstroCalcDialog::EphemerisList[i].sunCoord, sunWin);
1455 				// TODO: In some projections, we may need to test result and flip/mirror the angle, or deal with wrap-around effects.
1456 				// E.g., in cylindrical mode, the comet icon will flip as soon as the corresponding sun position wraps around the screen edge.
1457 				solarAngle=M_180_PIf*static_cast<float>(atan2(-(win[1]-sunWin[1]), win[0]-sunWin[0]));
1458 				// This will show projected positions and angles usable in labels.
1459 				debugStr = QString("Sun: %1/%2 Obj: %3/%4 -->%5").arg(QString::number(sunWin[0]), QString::number(sunWin[1]), QString::number(win[0]), QString::number(win[1]), QString::number(solarAngle));
1460 			}
1461 			//sPainter.drawSprite2dMode(static_cast<float>(win[0]), static_cast<float>(win[1]), size, 180.f+AstroCalcDialog::EphemerisList[i].solarAngle*M_180_PIf);
1462 			sPainter.drawSprite2dMode(static_cast<float>(win[0]), static_cast<float>(win[1]), size, 270.f-solarAngle);
1463 		}
1464 
1465 		if (showDates || showMagnitudes)
1466 		{
1467 			if (showSkippedData && ((i + 1)%dataStep)!=1 && dataStep!=1)
1468 				continue;
1469 
1470 			shift = 3.f + size/1.6f;
1471 			if (showDates && showMagnitudes)
1472 				info = QString("%1 (%2)").arg(AstroCalcDialog::EphemerisList[i].objDateStr, QString::number(AstroCalcDialog::EphemerisList[i].magnitude, 'f', 2));
1473 			if (showDates && !showMagnitudes)
1474 				info = AstroCalcDialog::EphemerisList[i].objDateStr;
1475 			if (!showDates && showMagnitudes)
1476 				info = QString::number(AstroCalcDialog::EphemerisList[i].magnitude, 'f', 2);
1477 
1478 			// Activate for debug labels.
1479 			//info=debugStr;
1480 			sPainter.drawText(AstroCalcDialog::EphemerisList[i].coord, info, 0, shift, shift, false);
1481 		}
1482 	}
1483 }
1484 
drawEphemerisLine(const StelCore * core)1485 void SolarSystem::drawEphemerisLine(const StelCore *core)
1486 {
1487 	const int size = AstroCalcDialog::EphemerisList.count();
1488 	if (size==0) return;
1489 
1490 	// The array of data is not empty - good news!
1491 	StelProjectorP prj;
1492 	if (getFlagEphemerisHorizontalCoordinates())
1493 		prj = core->getProjection(StelCore::FrameAltAz, StelCore::RefractionOff);
1494 	else
1495 		prj = core->getProjection(StelCore::FrameJ2000);
1496 	StelPainter sPainter(prj);
1497 	const float ppx = static_cast<float>(sPainter.getProjector()->getDevicePixelsPerPixel());
1498 
1499 	const float oldLineThickness=sPainter.getLineWidth();
1500 	const float lineThickness = getEphemerisLineThickness()*ppx;
1501 	if (!fuzzyEquals(lineThickness, oldLineThickness))
1502 		sPainter.setLineWidth(lineThickness);
1503 
1504 	Vec3f color;
1505 	QVector<Vec3d> vertexArray;
1506 	QVector<Vec4f> colorArray;
1507 	const int limit = getEphemerisDataLimit();
1508 	const int nsize = static_cast<int>(size/limit);
1509 	vertexArray.resize(nsize);
1510 	colorArray.resize(nsize);
1511 	for (int j=0; j<limit; j++)
1512 	{
1513 		for (int i =0; i < nsize; i++)
1514 		{
1515 			color = getEphemerisMarkerColor(AstroCalcDialog::EphemerisList[i + j*nsize].colorIndex);
1516 			colorArray[i]=Vec4f(color, 1.0f);
1517 			vertexArray[i]=AstroCalcDialog::EphemerisList[i + j*nsize].coord;
1518 		}
1519 		sPainter.drawPath(vertexArray, colorArray);
1520 	}
1521 
1522 	if (!fuzzyEquals(lineThickness, oldLineThickness))
1523 		sPainter.setLineWidth(oldLineThickness); // restore line thickness
1524 }
1525 
fillEphemerisDates()1526 void SolarSystem::fillEphemerisDates()
1527 {
1528 	const int fsize = AstroCalcDialog::EphemerisList.count();
1529 	if (fsize==0) return;
1530 
1531 	StelLocaleMgr* localeMgr = &StelApp::getInstance().getLocaleMgr();
1532 	const bool showSmartDates = getFlagEphemerisSmartDates();
1533 	double JD = AstroCalcDialog::EphemerisList.first().objDate;
1534 	bool withTime = (fsize>1 && (AstroCalcDialog::EphemerisList[1].objDate-JD<1.0));
1535 
1536 	int fYear, fMonth, fDay, sYear, sMonth, sDay, h, m, s;
1537 	QString info;
1538 	const double shift = StelApp::getInstance().getCore()->getUTCOffset(JD)*0.041666666666;
1539 	StelUtils::getDateFromJulianDay(JD+shift, &fYear, &fMonth, &fDay);
1540 	bool sFlag = true;
1541 	sYear = fYear;
1542 	sMonth = fMonth;
1543 	sDay = fDay;
1544 	const bool showSkippedData = getFlagEphemerisSkipData();
1545 	const int dataStep = getEphemerisDataStep();
1546 
1547 	for (int i = 0; i < fsize; i++)
1548 	{
1549 		JD = AstroCalcDialog::EphemerisList[i].objDate;
1550 		StelUtils::getDateFromJulianDay(JD+shift, &fYear, &fMonth, &fDay);
1551 
1552 		if (showSkippedData && ((i + 1)%dataStep)!=1 && dataStep!=1)
1553 			continue;
1554 
1555 		if (showSmartDates)
1556 		{
1557 			if (sFlag)
1558 				info = QString("%1").arg(fYear);
1559 
1560 			if (info.isEmpty() && !sFlag && fYear!=sYear)
1561 				info = QString("%1").arg(fYear);
1562 
1563 			if (!info.isEmpty())
1564 				info.append(QString("/%1").arg(localeMgr->romanMonthName(fMonth)));
1565 			else if (fMonth!=sMonth)
1566 				info = QString("%1").arg(localeMgr->romanMonthName(fMonth));
1567 
1568 			if (!info.isEmpty())
1569 				info.append(QString("/%1").arg(fDay));
1570 			else
1571 				info = QString("%1").arg(fDay);
1572 
1573 			if (withTime) // very short step
1574 			{
1575 				if (fDay==sDay && !sFlag)
1576 					info.clear();
1577 
1578 				StelUtils::getTimeFromJulianDay(JD+shift, &h, &m, &s);
1579 				if (!info.isEmpty())
1580 					info.append(QString(" %1:%2").arg(h).arg(m));
1581 				else
1582 					info = QString("%1:%2").arg(h).arg(m);
1583 			}
1584 
1585 			AstroCalcDialog::EphemerisList[i].objDateStr = info;
1586 			info.clear();
1587 			sYear = fYear;
1588 			sMonth = fMonth;
1589 			sDay = fDay;
1590 			sFlag = false;
1591 		}
1592 		else
1593 		{
1594 			// OK, let's use standard formats for date and time (as defined for whole planetarium)
1595 			if (withTime)
1596 				AstroCalcDialog::EphemerisList[i].objDateStr = QString("%1 %2").arg(localeMgr->getPrintableDateLocal(JD), localeMgr->getPrintableTimeLocal(JD));
1597 			else
1598 				AstroCalcDialog::EphemerisList[i].objDateStr = localeMgr->getPrintableDateLocal(JD);
1599 		}
1600 	}
1601 }
1602 
searchByEnglishName(QString planetEnglishName) const1603 PlanetP SolarSystem::searchByEnglishName(QString planetEnglishName) const
1604 {
1605 	for (const auto& p : systemPlanets)
1606 	{
1607 		if (p->getEnglishName().toUpper() == planetEnglishName.toUpper() || p->getCommonEnglishName().toUpper() == planetEnglishName.toUpper())
1608 			return p;
1609 	}
1610 	return PlanetP();
1611 }
1612 
searchMinorPlanetByEnglishName(QString planetEnglishName) const1613 PlanetP SolarSystem::searchMinorPlanetByEnglishName(QString planetEnglishName) const
1614 {
1615 	for (const auto& p : systemMinorBodies)
1616 	{
1617 		if (p->getCommonEnglishName().toUpper() == planetEnglishName.toUpper() || p->getEnglishName().toUpper() == planetEnglishName.toUpper())
1618 			return p;
1619 	}
1620 	return PlanetP();
1621 }
1622 
1623 
searchByNameI18n(const QString & planetNameI18) const1624 StelObjectP SolarSystem::searchByNameI18n(const QString& planetNameI18) const
1625 {
1626 	for (const auto& p : systemPlanets)
1627 	{
1628 		QString nativeName = p->getNativeNameI18n().toUpper();
1629 		if (p->getNameI18n().toUpper() == planetNameI18.toUpper() || (!nativeName.isEmpty() && nativeName == planetNameI18.toUpper()))
1630 			return qSharedPointerCast<StelObject>(p);
1631 	}
1632 	return StelObjectP();
1633 }
1634 
1635 
searchByName(const QString & name) const1636 StelObjectP SolarSystem::searchByName(const QString& name) const
1637 {
1638 	for (const auto& p : systemPlanets)
1639 	{
1640 		QString nativeName = p->getNativeName().toUpper();
1641 		if (p->getEnglishName().toUpper() == name.toUpper() || (!nativeName.isEmpty() && nativeName == name.toUpper()))
1642 			return qSharedPointerCast<StelObject>(p);
1643 	}
1644 	return StelObjectP();
1645 }
1646 
getPlanetVMagnitude(QString planetName,bool withExtinction) const1647 float SolarSystem::getPlanetVMagnitude(QString planetName, bool withExtinction) const
1648 {
1649 	PlanetP p = searchByEnglishName(planetName);
1650 	if (p.isNull()) // Possible was asked the common name of minor planet?
1651 		p = searchMinorPlanetByEnglishName(planetName);
1652 	float r = 0.f;
1653 	if (withExtinction)
1654 		r = p->getVMagnitudeWithExtinction(StelApp::getInstance().getCore());
1655 	else
1656 		r = p->getVMagnitude(StelApp::getInstance().getCore());
1657 	return r;
1658 }
1659 
getPlanetType(QString planetName) const1660 QString SolarSystem::getPlanetType(QString planetName) const
1661 {
1662 	PlanetP p = searchByEnglishName(planetName);
1663 	if (p.isNull()) // Possible was asked the common name of minor planet?
1664 		p = searchMinorPlanetByEnglishName(planetName);
1665 	if (p.isNull())
1666 		return QString("UNDEFINED");
1667 	return p->getPlanetTypeString();
1668 }
1669 
getDistanceToPlanet(QString planetName) const1670 double SolarSystem::getDistanceToPlanet(QString planetName) const
1671 {
1672 	PlanetP p = searchByEnglishName(planetName);
1673 	if (p.isNull()) // Possible was asked the common name of minor planet?
1674 		p = searchMinorPlanetByEnglishName(planetName);
1675 	return p->getDistance();
1676 }
1677 
getElongationForPlanet(QString planetName) const1678 double SolarSystem::getElongationForPlanet(QString planetName) const
1679 {
1680 	PlanetP p = searchByEnglishName(planetName);
1681 	if (p.isNull()) // Possible was asked the common name of minor planet?
1682 		p = searchMinorPlanetByEnglishName(planetName);
1683 	return p->getElongation(StelApp::getInstance().getCore()->getObserverHeliocentricEclipticPos());
1684 }
1685 
getPhaseAngleForPlanet(QString planetName) const1686 double SolarSystem::getPhaseAngleForPlanet(QString planetName) const
1687 {
1688 	PlanetP p = searchByEnglishName(planetName);
1689 	if (p.isNull()) // Possible was asked the common name of minor planet?
1690 		p = searchMinorPlanetByEnglishName(planetName);
1691 	return p->getPhaseAngle(StelApp::getInstance().getCore()->getObserverHeliocentricEclipticPos());
1692 }
1693 
getPhaseForPlanet(QString planetName) const1694 float SolarSystem::getPhaseForPlanet(QString planetName) const
1695 {
1696 	PlanetP p = searchByEnglishName(planetName);
1697 	if (p.isNull()) // Possible was asked the common name of minor planet?
1698 		p = searchMinorPlanetByEnglishName(planetName);
1699 	return p->getPhase(StelApp::getInstance().getCore()->getObserverHeliocentricEclipticPos());
1700 }
1701 
getObjectsList(QString objType) const1702 QStringList SolarSystem::getObjectsList(QString objType) const
1703 {
1704 	QStringList r;
1705 	if (objType.toLower()=="all")
1706 	{
1707 		r = listAllObjects(true);
1708 		// Remove the Sun
1709 		r.removeOne("Sun");
1710 		// Remove special objects
1711 		r.removeOne("Solar System Observer");
1712 		r.removeOne("Earth Observer");
1713 		r.removeOne("Mars Observer");
1714 		r.removeOne("Jupiter Observer");
1715 		r.removeOne("Saturn Observer");
1716 		r.removeOne("Uranus Observer");
1717 		r.removeOne("Neptune Observer");
1718 	}
1719 	else
1720 		r = listAllObjectsByType(objType, true);
1721 
1722 	return r;
1723 }
1724 
1725 // Search if any Planet is close to position given in earth equatorial position and return the distance
search(Vec3d pos,const StelCore * core) const1726 StelObjectP SolarSystem::search(Vec3d pos, const StelCore* core) const
1727 {
1728 	pos.normalize();
1729 	PlanetP closest;
1730 	double cos_angle_closest = 0.;
1731 	Vec3d equPos;
1732 
1733 	for (const auto& p : systemPlanets)
1734 	{
1735 		equPos = p->getEquinoxEquatorialPos(core);
1736 		equPos.normalize();
1737 		double cos_ang_dist = equPos*pos;
1738 		if (cos_ang_dist>cos_angle_closest)
1739 		{
1740 			closest = p;
1741 			cos_angle_closest = cos_ang_dist;
1742 		}
1743 	}
1744 
1745 	if (cos_angle_closest>0.999)
1746 	{
1747 		return qSharedPointerCast<StelObject>(closest);
1748 	}
1749 	else return StelObjectP();
1750 }
1751 
1752 // Return a QList containing the planets located inside the limFov circle around position vv
searchAround(const Vec3d & vv,double limitFov,const StelCore * core) const1753 QList<StelObjectP> SolarSystem::searchAround(const Vec3d& vv, double limitFov, const StelCore* core) const
1754 {
1755 	QList<StelObjectP> result;
1756 	if (!getFlagPlanets())
1757 		return result;
1758 
1759 	const bool withAberration=core->getUseAberration();
1760 	Vec3d v(vv);
1761 	v.normalize(); // TODO: start with vv already normalized?
1762 	if (withAberration)
1763 	{
1764 		Vec3d vel=core->getCurrentPlanet()->getHeliocentricEclipticVelocity();
1765 		StelCore::matVsop87ToJ2000.transfo(vel);
1766 		vel*=core->getAberrationFactor()*(AU/(86400.0*SPEED_OF_LIGHT));
1767 		v+=vel;
1768 		v.normalize();
1769 	}
1770 
1771 	double cosLimFov = std::cos(limitFov * M_PI/180.);
1772 	Vec3d equPos;
1773 	double cosAngularSize;
1774 
1775 	const QString weAreHere = core->getCurrentPlanet()->getEnglishName();
1776 	for (const auto& p : systemPlanets)
1777 	{
1778 		equPos = p->getJ2000EquatorialPos(core);
1779 		equPos.normalize();
1780 
1781 		cosAngularSize = std::cos(p->getSpheroidAngularSize(core) * M_PI/180.);
1782 
1783 		if (equPos*v>=std::min(cosLimFov, cosAngularSize) && p->getEnglishName()!=weAreHere)
1784 		{
1785 			result.append(qSharedPointerCast<StelObject>(p));
1786 		}
1787 	}
1788 	return result;
1789 }
1790 
1791 // Update i18 names from english names according to current sky culture translator
updateI18n()1792 void SolarSystem::updateI18n()
1793 {
1794 	const StelTranslator& trans = StelApp::getInstance().getLocaleMgr().getSkyTranslator();
1795 	for (const auto& p : qAsConst(systemPlanets))
1796 		p->translateName(trans);
1797 }
1798 
listMatchingObjects(const QString & objPrefix,int maxNbItem,bool useStartOfWords) const1799 QStringList SolarSystem::listMatchingObjects(const QString& objPrefix, int maxNbItem, bool useStartOfWords) const
1800 {
1801 	QStringList result;
1802 	if (getFlagPlanets())
1803 		result = StelObjectModule::listMatchingObjects(objPrefix, maxNbItem, useStartOfWords);
1804 	return result;
1805 }
1806 
setFlagTrails(bool b)1807 void SolarSystem::setFlagTrails(bool b)
1808 {
1809 	if (getFlagTrails() != b)
1810 	{
1811 		trailFader = b;
1812 		if (b)
1813 		{
1814 			allTrails->reset(maxTrailPoints);
1815 			recreateTrails();
1816 		}
1817 		emit trailsDisplayedChanged(b);
1818 	}
1819 }
1820 
getFlagTrails() const1821 bool SolarSystem::getFlagTrails() const
1822 {
1823 	return static_cast<bool>(trailFader);
1824 }
1825 
setMaxTrailPoints(int max)1826 void SolarSystem::setMaxTrailPoints(int max)
1827 {
1828 	if (maxTrailPoints != max)
1829 	{
1830 		maxTrailPoints = max;
1831 		allTrails->reset(max);
1832 		recreateTrails();
1833 		emit maxTrailPointsChanged(max);
1834 	}
1835 }
1836 
setMaxTrailTimeExtent(int max)1837 void SolarSystem::setMaxTrailTimeExtent(int max)
1838 {
1839 	if (maxTrailTimeExtent != max && maxTrailTimeExtent > 0)
1840 	{
1841 		maxTrailTimeExtent = max;
1842 		recreateTrails();
1843 		emit maxTrailTimeExtentChanged(max);
1844 	}
1845 }
1846 
setTrailsThickness(int v)1847 void SolarSystem::setTrailsThickness(int v)
1848 {
1849 	if (trailsThickness != v)
1850 	{
1851 		trailsThickness = v;
1852 		emit trailsThicknessChanged(v);
1853 	}
1854 }
1855 
setFlagHints(bool b)1856 void SolarSystem::setFlagHints(bool b)
1857 {
1858 	if (getFlagHints() != b)
1859 	{
1860 		for (const auto& p : qAsConst(systemPlanets))
1861 			p->setFlagHints(b);
1862 		emit flagHintsChanged(b);
1863 	}
1864 }
1865 
getFlagHints(void) const1866 bool SolarSystem::getFlagHints(void) const
1867 {
1868 	for (const auto& p : systemPlanets)
1869 	{
1870 		if (p->getFlagHints())
1871 			return true;
1872 	}
1873 	return false;
1874 }
1875 
setFlagLabels(bool b)1876 void SolarSystem::setFlagLabels(bool b)
1877 {
1878 	if (getFlagLabels() != b)
1879 	{
1880 		for (const auto& p : qAsConst(systemPlanets))
1881 			p->setFlagLabels(b);
1882 		emit labelsDisplayedChanged(b);
1883 	}
1884 }
1885 
getFlagLabels() const1886 bool SolarSystem::getFlagLabels() const
1887 {
1888 	for (const auto& p : systemPlanets)
1889 	{
1890 		if (p->getFlagLabels())
1891 			return true;
1892 	}
1893 	return false;
1894 }
1895 
setFlagOrbits(bool b)1896 void SolarSystem::setFlagOrbits(bool b)
1897 {
1898 	bool old = flagOrbits;
1899 	flagOrbits = b;
1900 	bool flagPlanetsOnly = getFlagPlanetsOrbitsOnly();
1901 	if (!b || !selected || selected==sun)
1902 	{
1903 		if (flagPlanetsOnly)
1904 		{
1905 			for (const auto& p : qAsConst(systemPlanets))
1906 			{
1907 				if (p->getPlanetType()==Planet::isPlanet)
1908 					p->setFlagOrbits(b);
1909 				else
1910 					p->setFlagOrbits(false);
1911 			}
1912 		}
1913 		else
1914 		{
1915 			for (const auto& p : qAsConst(systemPlanets))
1916 				p->setFlagOrbits(b);
1917 		}
1918 	}
1919 	else if (getFlagIsolatedOrbits()) // If a Planet is selected and orbits are on, fade out non-selected ones
1920 	{
1921 		if (flagPlanetsOnly)
1922 		{
1923 			for (const auto& p : qAsConst(systemPlanets))
1924 			{
1925 				if (selected == p && p->getPlanetType()==Planet::isPlanet)
1926 					p->setFlagOrbits(b);
1927 				else
1928 					p->setFlagOrbits(false);
1929 			}
1930 		}
1931 		else
1932 		{
1933 			for (const auto& p : qAsConst(systemPlanets))
1934 			{
1935 				if (selected == p)
1936 					p->setFlagOrbits(b);
1937 				else
1938 					p->setFlagOrbits(false);
1939 			}
1940 		}
1941 	}
1942 	else
1943 	{
1944 		// A planet is selected and orbits are on - draw orbits for the planet and their moons
1945 		for (const auto& p : qAsConst(systemPlanets))
1946 		{
1947 			if (selected == p || selected == p->parent)
1948 				p->setFlagOrbits(b);
1949 			else
1950 				p->setFlagOrbits(false);
1951 		}
1952 	}
1953 	if(old != flagOrbits)
1954 		emit flagOrbitsChanged(flagOrbits);
1955 }
1956 
setFlagLightTravelTime(bool b)1957 void SolarSystem::setFlagLightTravelTime(bool b)
1958 {
1959 	if(b!=flagLightTravelTime)
1960 	{
1961 		flagLightTravelTime = b;
1962 		emit flagLightTravelTimeChanged(b);
1963 	}
1964 }
1965 
setFlagShowObjSelfShadows(bool b)1966 void SolarSystem::setFlagShowObjSelfShadows(bool b)
1967 {
1968 	if(b!=flagShowObjSelfShadows)
1969 	{
1970 		flagShowObjSelfShadows = b;
1971 		if(!b)
1972 			Planet::deinitFBO();
1973 		emit flagShowObjSelfShadowsChanged(b);
1974 	}
1975 }
1976 
setSelected(PlanetP obj)1977 void SolarSystem::setSelected(PlanetP obj)
1978 {
1979 	if (obj && obj->getType() == "Planet")
1980 	{
1981 		selected = obj;
1982 		selectedSSO.push_back(obj);
1983 	}
1984 	else
1985 		selected.clear();
1986 	// Undraw other objects hints, orbit, trails etc..
1987 	setFlagHints(getFlagHints());
1988 	setFlagOrbits(getFlagOrbits());
1989 }
1990 
1991 
update(double deltaTime)1992 void SolarSystem::update(double deltaTime)
1993 {
1994 	trailFader.update(static_cast<int>(deltaTime*1000));
1995 	if (trailFader.getInterstate()>0.f)
1996 	{
1997 		allTrails->update();
1998 	}
1999 
2000 	for (const auto& p : qAsConst(systemPlanets))
2001 	{
2002 		p->update(static_cast<int>(deltaTime*1000));
2003 	}
2004 }
2005 
2006 // is a lunar eclipse close at hand?
nearLunarEclipse() const2007 bool SolarSystem::nearLunarEclipse() const
2008 {
2009 	// TODO: could replace with simpler test
2010 	// TODO Source?
2011 
2012 	const Vec3d sun = getSun()->getAberrationPush();
2013 	const Vec3d e = getEarth()->getEclipticPos();
2014 	const Vec3d m = getMoon()->getEclipticPos();  // relative to earth
2015 	const Vec3d mh = getMoon()->getHeliocentricEclipticPos();  // relative to sun
2016 
2017 	// shadow location at earth + moon distance along earth vector from (aberrated) sun
2018 	Vec3d en = e-sun;
2019 	en.normalize();
2020 	Vec3d shadow = en * (e.length() + m.length());
2021 
2022 	// find shadow radii in AU
2023 	double r_penumbra = shadow.length()*702378.1/AU/e.length() - 696000./AU;
2024 
2025 	// modify shadow location for scaled moon
2026 	Vec3d mdist = shadow - mh;
2027 	if(mdist.length() > r_penumbra + 2000./AU) return false;   // not visible so don't bother drawing
2028 
2029 	return true;
2030 }
2031 
listAllObjects(bool inEnglish) const2032 QStringList SolarSystem::listAllObjects(bool inEnglish) const
2033 {
2034 	QStringList result;
2035 	if (inEnglish)
2036 	{
2037 		for (const auto& p : systemPlanets)
2038 		{
2039 			result << p->getEnglishName();
2040 		}
2041 	}
2042 	else
2043 	{
2044 		for (const auto& p : systemPlanets)
2045 		{
2046 			result << p->getNameI18n();
2047 			if (!p->getNativeNameI18n().isEmpty())
2048 				result << p->getNativeNameI18n() << p->getNativeName();
2049 		}
2050 	}
2051 	return result;
2052 }
2053 
listAllObjectsByType(const QString & objType,bool inEnglish) const2054 QStringList SolarSystem::listAllObjectsByType(const QString &objType, bool inEnglish) const
2055 {
2056 	QStringList result;
2057 	if (inEnglish)
2058 	{
2059 		for (const auto& p : systemPlanets)
2060 		{
2061 			if (p->getPlanetTypeString()==objType)
2062 				result << p->getEnglishName();
2063 		}
2064 	}
2065 	else
2066 	{
2067 		for (const auto& p : systemPlanets)
2068 		{
2069 			if (p->getPlanetTypeString()==objType)
2070 				result << p->getNameI18n();
2071 		}
2072 	}
2073 	return result;
2074 }
2075 
selectedObjectChange(StelModule::StelModuleSelectAction)2076 void SolarSystem::selectedObjectChange(StelModule::StelModuleSelectAction)
2077 {
2078 	const QList<StelObjectP> newSelected = GETSTELMODULE(StelObjectMgr)->getSelectedObject("Planet");
2079 	if (!newSelected.empty())
2080 	{
2081 		setSelected(qSharedPointerCast<Planet>(newSelected[0]));
2082 		if (getFlagIsolatedTrails())
2083 			recreateTrails();
2084 	}
2085 	else
2086 		setSelected("");
2087 }
2088 
2089 // Activate/Deactivate planets display
setFlagPlanets(bool b)2090 void SolarSystem::setFlagPlanets(bool b)
2091 {
2092 	if (b!=flagShow)
2093 	{
2094 		flagShow=b;
2095 		emit flagPlanetsDisplayedChanged(b);
2096 	}
2097 }
2098 
getFlagPlanets(void) const2099 bool SolarSystem::getFlagPlanets(void) const
2100 {
2101 	return flagShow;
2102 }
2103 
setFlagEphemerisMarkers(bool b)2104 void SolarSystem::setFlagEphemerisMarkers(bool b)
2105 {
2106 	if (b!=ephemerisMarkersDisplayed)
2107 	{
2108 		ephemerisMarkersDisplayed=b;
2109 		conf->setValue("astrocalc/flag_ephemeris_markers", b); // Immediate saving of state
2110 		emit ephemerisMarkersChanged(b);
2111 	}
2112 }
2113 
getFlagEphemerisMarkers() const2114 bool SolarSystem::getFlagEphemerisMarkers() const
2115 {
2116 	return ephemerisMarkersDisplayed;
2117 }
2118 
setFlagEphemerisLine(bool b)2119 void SolarSystem::setFlagEphemerisLine(bool b)
2120 {
2121 	if (b!=ephemerisLineDisplayed)
2122 	{
2123 		ephemerisLineDisplayed=b;
2124 		conf->setValue("astrocalc/flag_ephemeris_line", b); // Immediate saving of state
2125 		emit ephemerisLineChanged(b);
2126 	}
2127 }
2128 
getFlagEphemerisLine() const2129 bool SolarSystem::getFlagEphemerisLine() const
2130 {
2131 	return ephemerisLineDisplayed;
2132 }
2133 
getFlagEphemerisAlwaysOn() const2134 bool SolarSystem::getFlagEphemerisAlwaysOn() const
2135 {
2136 	return ephemerisAlwaysOn;
2137 }
2138 
setFlagEphemerisAlwaysOn(bool b)2139 void SolarSystem::setFlagEphemerisAlwaysOn(bool b)
2140 {
2141 	if (b != ephemerisAlwaysOn)
2142 	{
2143 		ephemerisAlwaysOn = b;
2144 		conf->setValue("astrocalc/flag_ephemeris_alwayson", b); // Immediate saving of state
2145 		emit ephemerisAlwaysOnChanged(b);
2146 	}
2147 }
2148 
setFlagEphemerisHorizontalCoordinates(bool b)2149 void SolarSystem::setFlagEphemerisHorizontalCoordinates(bool b)
2150 {
2151 	if (b!=ephemerisHorizontalCoordinates)
2152 	{
2153 		ephemerisHorizontalCoordinates=b;
2154 		conf->setValue("astrocalc/flag_ephemeris_horizontal", b); // Immediate saving of state
2155 		emit ephemerisHorizontalCoordinatesChanged(b);
2156 	}
2157 }
2158 
getFlagEphemerisHorizontalCoordinates() const2159 bool SolarSystem::getFlagEphemerisHorizontalCoordinates() const
2160 {
2161 	return ephemerisHorizontalCoordinates;
2162 }
2163 
setFlagEphemerisDates(bool b)2164 void SolarSystem::setFlagEphemerisDates(bool b)
2165 {
2166 	if (b!=ephemerisDatesDisplayed)
2167 	{
2168 		ephemerisDatesDisplayed=b;
2169 		conf->setValue("astrocalc/flag_ephemeris_dates", b); // Immediate saving of state
2170 		emit ephemerisDatesChanged(b);
2171 	}
2172 }
2173 
getFlagEphemerisDates() const2174 bool SolarSystem::getFlagEphemerisDates() const
2175 {
2176 	return ephemerisDatesDisplayed;
2177 }
2178 
setFlagEphemerisMagnitudes(bool b)2179 void SolarSystem::setFlagEphemerisMagnitudes(bool b)
2180 {
2181 	if (b!=ephemerisMagnitudesDisplayed)
2182 	{
2183 		ephemerisMagnitudesDisplayed=b;
2184 		conf->setValue("astrocalc/flag_ephemeris_magnitudes", b); // Immediate saving of state
2185 		emit ephemerisMagnitudesChanged(b);
2186 	}
2187 }
2188 
getFlagEphemerisMagnitudes() const2189 bool SolarSystem::getFlagEphemerisMagnitudes() const
2190 {
2191 	return ephemerisMagnitudesDisplayed;
2192 }
2193 
setFlagEphemerisSkipData(bool b)2194 void SolarSystem::setFlagEphemerisSkipData(bool b)
2195 {
2196 	if (b!=ephemerisSkipDataDisplayed)
2197 	{
2198 		ephemerisSkipDataDisplayed=b;
2199 		conf->setValue("astrocalc/flag_ephemeris_skip_data", b); // Immediate saving of state
2200 		emit ephemerisSkipDataChanged(b);
2201 	}
2202 }
2203 
getFlagEphemerisSkipData() const2204 bool SolarSystem::getFlagEphemerisSkipData() const
2205 {
2206 	return ephemerisSkipDataDisplayed;
2207 }
2208 
setFlagEphemerisSkipMarkers(bool b)2209 void SolarSystem::setFlagEphemerisSkipMarkers(bool b)
2210 {
2211 	if (b!=ephemerisSkipMarkersDisplayed)
2212 	{
2213 		ephemerisSkipMarkersDisplayed=b;
2214 		conf->setValue("astrocalc/flag_ephemeris_skip_markers", b); // Immediate saving of state
2215 		emit ephemerisSkipMarkersChanged(b);
2216 	}
2217 }
2218 
getFlagEphemerisSkipMarkers() const2219 bool SolarSystem::getFlagEphemerisSkipMarkers() const
2220 {
2221 	return ephemerisSkipMarkersDisplayed;
2222 }
2223 
setFlagEphemerisSmartDates(bool b)2224 void SolarSystem::setFlagEphemerisSmartDates(bool b)
2225 {
2226 	if (b!=ephemerisSmartDatesDisplayed)
2227 	{
2228 		ephemerisSmartDatesDisplayed=b;
2229 		conf->setValue("astrocalc/flag_ephemeris_smart_dates", b); // Immediate saving of state
2230 		emit ephemerisSmartDatesChanged(b);
2231 	}
2232 }
2233 
getFlagEphemerisSmartDates() const2234 bool SolarSystem::getFlagEphemerisSmartDates() const
2235 {
2236 	return ephemerisSmartDatesDisplayed;
2237 }
2238 
setFlagEphemerisScaleMarkers(bool b)2239 void SolarSystem::setFlagEphemerisScaleMarkers(bool b)
2240 {
2241 	if (b!=ephemerisScaleMarkersDisplayed)
2242 	{
2243 		ephemerisScaleMarkersDisplayed=b;
2244 		conf->setValue("astrocalc/flag_ephemeris_scale_markers", b); // Immediate saving of state
2245 		emit ephemerisScaleMarkersChanged(b);
2246 	}
2247 }
2248 
getFlagEphemerisScaleMarkers() const2249 bool SolarSystem::getFlagEphemerisScaleMarkers() const
2250 {
2251 	return ephemerisScaleMarkersDisplayed;
2252 }
2253 
setEphemerisDataStep(int step)2254 void SolarSystem::setEphemerisDataStep(int step)
2255 {
2256 	ephemerisDataStep = step;
2257 	// automatic saving of the setting
2258 	conf->setValue("astrocalc/ephemeris_data_step", step);
2259 	emit ephemerisDataStepChanged(step);
2260 }
2261 
getEphemerisDataStep() const2262 int SolarSystem::getEphemerisDataStep() const
2263 {
2264 	return ephemerisDataStep;
2265 }
2266 
setEphemerisDataLimit(int limit)2267 void SolarSystem::setEphemerisDataLimit(int limit)
2268 {
2269 	ephemerisDataLimit = limit;
2270 	emit ephemerisDataLimitChanged(limit);
2271 }
2272 
getEphemerisDataLimit() const2273 int SolarSystem::getEphemerisDataLimit() const
2274 {
2275 	return ephemerisDataLimit;
2276 }
2277 
setEphemerisLineThickness(int v)2278 void SolarSystem::setEphemerisLineThickness(int v)
2279 {
2280 	ephemerisLineThickness = v;
2281 	// automatic saving of the setting
2282 	conf->setValue("astrocalc/ephemeris_line_thickness", v);
2283 	emit ephemerisLineThicknessChanged(v);
2284 }
2285 
getEphemerisLineThickness() const2286 int SolarSystem::getEphemerisLineThickness() const
2287 {
2288 	return ephemerisLineThickness;
2289 }
2290 
setEphemerisGenericMarkerColor(const Vec3f & color)2291 void SolarSystem::setEphemerisGenericMarkerColor(const Vec3f& color)
2292 {
2293 	if (color!=ephemerisGenericMarkerColor)
2294 	{
2295 		ephemerisGenericMarkerColor = color;
2296 		emit ephemerisGenericMarkerColorChanged(color);
2297 	}
2298 }
2299 
getEphemerisGenericMarkerColor() const2300 Vec3f SolarSystem::getEphemerisGenericMarkerColor() const
2301 {
2302 	return ephemerisGenericMarkerColor;
2303 }
2304 
setEphemerisSecondaryMarkerColor(const Vec3f & color)2305 void SolarSystem::setEphemerisSecondaryMarkerColor(const Vec3f& color)
2306 {
2307 	if (color!=ephemerisSecondaryMarkerColor)
2308 	{
2309 		ephemerisSecondaryMarkerColor = color;
2310 		emit ephemerisSecondaryMarkerColorChanged(color);
2311 	}
2312 }
2313 
getEphemerisSecondaryMarkerColor() const2314 Vec3f SolarSystem::getEphemerisSecondaryMarkerColor() const
2315 {
2316 	return ephemerisSecondaryMarkerColor;
2317 }
2318 
setEphemerisSelectedMarkerColor(const Vec3f & color)2319 void SolarSystem::setEphemerisSelectedMarkerColor(const Vec3f& color)
2320 {
2321 	if (color!=ephemerisSelectedMarkerColor)
2322 	{
2323 		ephemerisSelectedMarkerColor = color;
2324 		emit ephemerisSelectedMarkerColorChanged(color);
2325 	}
2326 }
2327 
getEphemerisSelectedMarkerColor() const2328 Vec3f SolarSystem::getEphemerisSelectedMarkerColor() const
2329 {
2330 	return ephemerisSelectedMarkerColor;
2331 }
2332 
setEphemerisMercuryMarkerColor(const Vec3f & color)2333 void SolarSystem::setEphemerisMercuryMarkerColor(const Vec3f& color)
2334 {
2335 	if (color!=ephemerisMercuryMarkerColor)
2336 	{
2337 		ephemerisMercuryMarkerColor = color;
2338 		emit ephemerisMercuryMarkerColorChanged(color);
2339 	}
2340 }
2341 
getEphemerisMercuryMarkerColor() const2342 Vec3f SolarSystem::getEphemerisMercuryMarkerColor() const
2343 {
2344 	return ephemerisMercuryMarkerColor;
2345 }
2346 
setEphemerisVenusMarkerColor(const Vec3f & color)2347 void SolarSystem::setEphemerisVenusMarkerColor(const Vec3f& color)
2348 {
2349 	if (color!=ephemerisVenusMarkerColor)
2350 	{
2351 		ephemerisVenusMarkerColor = color;
2352 		emit ephemerisVenusMarkerColorChanged(color);
2353 	}
2354 }
2355 
getEphemerisVenusMarkerColor() const2356 Vec3f SolarSystem::getEphemerisVenusMarkerColor() const
2357 {
2358 	return ephemerisVenusMarkerColor;
2359 }
2360 
setEphemerisMarsMarkerColor(const Vec3f & color)2361 void SolarSystem::setEphemerisMarsMarkerColor(const Vec3f& color)
2362 {
2363 	if (color!=ephemerisMarsMarkerColor)
2364 	{
2365 		ephemerisMarsMarkerColor = color;
2366 		emit ephemerisMarsMarkerColorChanged(color);
2367 	}
2368 }
2369 
getEphemerisMarsMarkerColor() const2370 Vec3f SolarSystem::getEphemerisMarsMarkerColor() const
2371 {
2372 	return ephemerisMarsMarkerColor;
2373 }
2374 
setEphemerisJupiterMarkerColor(const Vec3f & color)2375 void SolarSystem::setEphemerisJupiterMarkerColor(const Vec3f& color)
2376 {
2377 	if (color!=ephemerisJupiterMarkerColor)
2378 	{
2379 		ephemerisJupiterMarkerColor = color;
2380 		emit ephemerisJupiterMarkerColorChanged(color);
2381 	}
2382 }
2383 
getEphemerisJupiterMarkerColor() const2384 Vec3f SolarSystem::getEphemerisJupiterMarkerColor() const
2385 {
2386 	return ephemerisJupiterMarkerColor;
2387 }
2388 
setEphemerisSaturnMarkerColor(const Vec3f & color)2389 void SolarSystem::setEphemerisSaturnMarkerColor(const Vec3f& color)
2390 {
2391 	if (color!=ephemerisSaturnMarkerColor)
2392 	{
2393 		ephemerisSaturnMarkerColor = color;
2394 		emit ephemerisSaturnMarkerColorChanged(color);
2395 	}
2396 }
2397 
getEphemerisSaturnMarkerColor() const2398 Vec3f SolarSystem::getEphemerisSaturnMarkerColor() const
2399 {
2400 	return ephemerisSaturnMarkerColor;
2401 }
2402 
setFlagNativePlanetNames(bool b)2403 void SolarSystem::setFlagNativePlanetNames(bool b)
2404 {
2405 	if (b!=flagNativePlanetNames)
2406 	{
2407 		flagNativePlanetNames=b;
2408 		for (const auto& p : qAsConst(systemPlanets))
2409 		{
2410 			if (p->getPlanetType()==Planet::isPlanet || p->getPlanetType()==Planet::isMoon || p->getPlanetType()==Planet::isStar)
2411 				p->setFlagNativeName(flagNativePlanetNames);
2412 		}
2413 		updateI18n();
2414 		emit flagNativePlanetNamesChanged(b);
2415 	}
2416 }
2417 
getFlagNativePlanetNames() const2418 bool SolarSystem::getFlagNativePlanetNames() const
2419 {
2420 	return flagNativePlanetNames;
2421 }
2422 
setFlagIsolatedTrails(bool b)2423 void SolarSystem::setFlagIsolatedTrails(bool b)
2424 {
2425 	if(b!=flagIsolatedTrails)
2426 	{
2427 		flagIsolatedTrails = b;
2428 		recreateTrails();
2429 		emit flagIsolatedTrailsChanged(b);
2430 	}
2431 }
2432 
getFlagIsolatedTrails() const2433 bool SolarSystem::getFlagIsolatedTrails() const
2434 {
2435 	return flagIsolatedTrails;
2436 }
2437 
getNumberIsolatedTrails() const2438 int SolarSystem::getNumberIsolatedTrails() const
2439 {
2440 	return numberIsolatedTrails;
2441 }
2442 
setNumberIsolatedTrails(int n)2443 void SolarSystem::setNumberIsolatedTrails(int n)
2444 {
2445 	// [1..5] - valid range for trails
2446 	numberIsolatedTrails = qBound(1, n, 5);
2447 
2448 	if (getFlagIsolatedTrails())
2449 		recreateTrails();
2450 
2451 	emit numberIsolatedTrailsChanged(numberIsolatedTrails);
2452 }
2453 
setFlagIsolatedOrbits(bool b)2454 void SolarSystem::setFlagIsolatedOrbits(bool b)
2455 {
2456 	if(b!=flagIsolatedOrbits)
2457 	{
2458 		flagIsolatedOrbits = b;
2459 		emit flagIsolatedOrbitsChanged(b);
2460 		// Reinstall flag for orbits to renew visibility of orbits
2461 		setFlagOrbits(getFlagOrbits());
2462 	}
2463 }
2464 
getFlagIsolatedOrbits() const2465 bool SolarSystem::getFlagIsolatedOrbits() const
2466 {
2467 	return flagIsolatedOrbits;
2468 }
2469 
setFlagPlanetsOrbitsOnly(bool b)2470 void SolarSystem::setFlagPlanetsOrbitsOnly(bool b)
2471 {
2472 	if(b!=flagPlanetsOrbitsOnly)
2473 	{
2474 		flagPlanetsOrbitsOnly = b;
2475 		emit flagPlanetsOrbitsOnlyChanged(b);
2476 		// Reinstall flag for orbits to renew visibility of orbits
2477 		setFlagOrbits(getFlagOrbits());
2478 	}
2479 }
2480 
getFlagPlanetsOrbitsOnly() const2481 bool SolarSystem::getFlagPlanetsOrbitsOnly() const
2482 {
2483 	return flagPlanetsOrbitsOnly;
2484 }
2485 
2486 // Set/Get planets names color
setLabelsColor(const Vec3f & c)2487 void SolarSystem::setLabelsColor(const Vec3f& c)
2488 {
2489 	if (c!=Planet::getLabelColor())
2490 	{
2491 		Planet::setLabelColor(c);
2492 		emit labelsColorChanged(c);
2493 	}
2494 }
2495 
getLabelsColor(void) const2496 Vec3f SolarSystem::getLabelsColor(void) const
2497 {
2498 	return Planet::getLabelColor();
2499 }
2500 
2501 // Set/Get orbits lines color
setOrbitsColor(const Vec3f & c)2502 void SolarSystem::setOrbitsColor(const Vec3f& c)
2503 {
2504 	if (c!=Planet::getOrbitColor())
2505 	{
2506 		Planet::setOrbitColor(c);
2507 		emit orbitsColorChanged(c);
2508 	}
2509 }
2510 
getOrbitsColor(void) const2511 Vec3f SolarSystem::getOrbitsColor(void) const
2512 {
2513 	return Planet::getOrbitColor();
2514 }
2515 
setMajorPlanetsOrbitsColor(const Vec3f & c)2516 void SolarSystem::setMajorPlanetsOrbitsColor(const Vec3f &c)
2517 {
2518 	if (c!=Planet::getMajorPlanetOrbitColor())
2519 	{
2520 		Planet::setMajorPlanetOrbitColor(c);
2521 		emit majorPlanetsOrbitsColorChanged(c);
2522 	}
2523 }
2524 
getMajorPlanetsOrbitsColor(void) const2525 Vec3f SolarSystem::getMajorPlanetsOrbitsColor(void) const
2526 {
2527 	return Planet::getMajorPlanetOrbitColor();
2528 }
2529 
setMinorPlanetsOrbitsColor(const Vec3f & c)2530 void SolarSystem::setMinorPlanetsOrbitsColor(const Vec3f &c)
2531 {
2532 	if (c!=Planet::getMinorPlanetOrbitColor())
2533 	{
2534 		Planet::setMinorPlanetOrbitColor(c);
2535 		emit minorPlanetsOrbitsColorChanged(c);
2536 	}
2537 }
2538 
getMinorPlanetsOrbitsColor(void) const2539 Vec3f SolarSystem::getMinorPlanetsOrbitsColor(void) const
2540 {
2541 	return Planet::getMinorPlanetOrbitColor();
2542 }
2543 
setDwarfPlanetsOrbitsColor(const Vec3f & c)2544 void SolarSystem::setDwarfPlanetsOrbitsColor(const Vec3f &c)
2545 {
2546 	if (c!=Planet::getDwarfPlanetOrbitColor())
2547 	{
2548 		Planet::setDwarfPlanetOrbitColor(c);
2549 		emit dwarfPlanetsOrbitsColorChanged(c);
2550 	}
2551 }
2552 
getDwarfPlanetsOrbitsColor(void) const2553 Vec3f SolarSystem::getDwarfPlanetsOrbitsColor(void) const
2554 {
2555 	return Planet::getDwarfPlanetOrbitColor();
2556 }
2557 
setMoonsOrbitsColor(const Vec3f & c)2558 void SolarSystem::setMoonsOrbitsColor(const Vec3f &c)
2559 {
2560 	if (c!=Planet::getMoonOrbitColor())
2561 	{
2562 		Planet::setMoonOrbitColor(c);
2563 		emit moonsOrbitsColorChanged(c);
2564 	}
2565 }
2566 
getMoonsOrbitsColor(void) const2567 Vec3f SolarSystem::getMoonsOrbitsColor(void) const
2568 {
2569 	return Planet::getMoonOrbitColor();
2570 }
2571 
setCubewanosOrbitsColor(const Vec3f & c)2572 void SolarSystem::setCubewanosOrbitsColor(const Vec3f &c)
2573 {
2574 	if (c!=Planet::getCubewanoOrbitColor())
2575 	{
2576 		Planet::setCubewanoOrbitColor(c);
2577 		emit cubewanosOrbitsColorChanged(c);
2578 	}
2579 }
2580 
getCubewanosOrbitsColor(void) const2581 Vec3f SolarSystem::getCubewanosOrbitsColor(void) const
2582 {
2583 	return Planet::getCubewanoOrbitColor();
2584 }
2585 
setPlutinosOrbitsColor(const Vec3f & c)2586 void SolarSystem::setPlutinosOrbitsColor(const Vec3f &c)
2587 {
2588 	if (c!=Planet::getPlutinoOrbitColor())
2589 	{
2590 		Planet::setPlutinoOrbitColor(c);
2591 		emit plutinosOrbitsColorChanged(c);
2592 	}
2593 }
2594 
getPlutinosOrbitsColor(void) const2595 Vec3f SolarSystem::getPlutinosOrbitsColor(void) const
2596 {
2597 	return Planet::getPlutinoOrbitColor();
2598 }
2599 
setScatteredDiskObjectsOrbitsColor(const Vec3f & c)2600 void SolarSystem::setScatteredDiskObjectsOrbitsColor(const Vec3f &c)
2601 {
2602 	if (c!=Planet::getScatteredDiscObjectOrbitColor())
2603 	{
2604 		Planet::setScatteredDiscObjectOrbitColor(c);
2605 		emit scatteredDiskObjectsOrbitsColorChanged(c);
2606 	}
2607 }
2608 
getScatteredDiskObjectsOrbitsColor(void) const2609 Vec3f SolarSystem::getScatteredDiskObjectsOrbitsColor(void) const
2610 {
2611 	return Planet::getScatteredDiscObjectOrbitColor();
2612 }
2613 
setOortCloudObjectsOrbitsColor(const Vec3f & c)2614 void SolarSystem::setOortCloudObjectsOrbitsColor(const Vec3f &c)
2615 {
2616 	if (c!=Planet::getOortCloudObjectOrbitColor())
2617 	{
2618 		Planet::setOortCloudObjectOrbitColor(c);
2619 		emit oortCloudObjectsOrbitsColorChanged(c);
2620 	}
2621 }
2622 
getOortCloudObjectsOrbitsColor(void) const2623 Vec3f SolarSystem::getOortCloudObjectsOrbitsColor(void) const
2624 {
2625 	return Planet::getOortCloudObjectOrbitColor();
2626 }
2627 
setCometsOrbitsColor(const Vec3f & c)2628 void SolarSystem::setCometsOrbitsColor(const Vec3f& c)
2629 {
2630 	if (c!=Planet::getCometOrbitColor())
2631 	{
2632 		Planet::setCometOrbitColor(c);
2633 		emit cometsOrbitsColorChanged(c);
2634 	}
2635 }
2636 
getCometsOrbitsColor(void) const2637 Vec3f SolarSystem::getCometsOrbitsColor(void) const
2638 {
2639 	return Planet::getCometOrbitColor();
2640 }
2641 
setSednoidsOrbitsColor(const Vec3f & c)2642 void SolarSystem::setSednoidsOrbitsColor(const Vec3f& c)
2643 {
2644 	if (c!=Planet::getSednoidOrbitColor())
2645 	{
2646 		Planet::setSednoidOrbitColor(c);
2647 		emit sednoidsOrbitsColorChanged(c);
2648 	}
2649 }
2650 
getSednoidsOrbitsColor(void) const2651 Vec3f SolarSystem::getSednoidsOrbitsColor(void) const
2652 {
2653 	return Planet::getSednoidOrbitColor();
2654 }
2655 
setInterstellarOrbitsColor(const Vec3f & c)2656 void SolarSystem::setInterstellarOrbitsColor(const Vec3f& c)
2657 {
2658 	if (c!=Planet::getInterstellarOrbitColor())
2659 	{
2660 		Planet::setInterstellarOrbitColor(c);
2661 		emit interstellarOrbitsColorChanged(c);
2662 	}
2663 }
2664 
getInterstellarOrbitsColor(void) const2665 Vec3f SolarSystem::getInterstellarOrbitsColor(void) const
2666 {
2667 	return Planet::getInterstellarOrbitColor();
2668 }
2669 
setMercuryOrbitColor(const Vec3f & c)2670 void SolarSystem::setMercuryOrbitColor(const Vec3f &c)
2671 {
2672 	if (c!=Planet::getMercuryOrbitColor())
2673 	{
2674 		Planet::setMercuryOrbitColor(c);
2675 		emit mercuryOrbitColorChanged(c);
2676 	}
2677 }
2678 
getMercuryOrbitColor(void) const2679 Vec3f SolarSystem::getMercuryOrbitColor(void) const
2680 {
2681 	return Planet::getMercuryOrbitColor();
2682 }
2683 
setVenusOrbitColor(const Vec3f & c)2684 void SolarSystem::setVenusOrbitColor(const Vec3f &c)
2685 {
2686 	if (c!=Planet::getVenusOrbitColor())
2687 	{
2688 		Planet::setVenusOrbitColor(c);
2689 		emit venusOrbitColorChanged(c);
2690 	}
2691 }
2692 
getVenusOrbitColor(void) const2693 Vec3f SolarSystem::getVenusOrbitColor(void) const
2694 {
2695 	return Planet::getVenusOrbitColor();
2696 }
2697 
setEarthOrbitColor(const Vec3f & c)2698 void SolarSystem::setEarthOrbitColor(const Vec3f &c)
2699 {
2700 	if (c!=Planet::getEarthOrbitColor())
2701 	{
2702 		Planet::setEarthOrbitColor(c);
2703 		emit earthOrbitColorChanged(c);
2704 	}
2705 }
2706 
getEarthOrbitColor(void) const2707 Vec3f SolarSystem::getEarthOrbitColor(void) const
2708 {
2709 	return Planet::getEarthOrbitColor();
2710 }
2711 
setMarsOrbitColor(const Vec3f & c)2712 void SolarSystem::setMarsOrbitColor(const Vec3f &c)
2713 {
2714 	if (c!=Planet::getMarsOrbitColor())
2715 	{
2716 		Planet::setMarsOrbitColor(c);
2717 		emit marsOrbitColorChanged(c);
2718 	}
2719 }
2720 
getMarsOrbitColor(void) const2721 Vec3f SolarSystem::getMarsOrbitColor(void) const
2722 {
2723 	return Planet::getMarsOrbitColor();
2724 }
2725 
setJupiterOrbitColor(const Vec3f & c)2726 void SolarSystem::setJupiterOrbitColor(const Vec3f &c)
2727 {
2728 	if (c!=Planet::getJupiterOrbitColor())
2729 	{
2730 		Planet::setJupiterOrbitColor(c);
2731 		emit jupiterOrbitColorChanged(c);
2732 	}
2733 }
2734 
getJupiterOrbitColor(void) const2735 Vec3f SolarSystem::getJupiterOrbitColor(void) const
2736 {
2737 	return Planet::getJupiterOrbitColor();
2738 }
2739 
setSaturnOrbitColor(const Vec3f & c)2740 void SolarSystem::setSaturnOrbitColor(const Vec3f &c)
2741 {
2742 	if (c!=Planet::getSaturnOrbitColor())
2743 	{
2744 		Planet::setSaturnOrbitColor(c);
2745 		emit saturnOrbitColorChanged(c);
2746 	}
2747 }
2748 
getSaturnOrbitColor(void) const2749 Vec3f SolarSystem::getSaturnOrbitColor(void) const
2750 {
2751 	return Planet::getSaturnOrbitColor();
2752 }
2753 
setUranusOrbitColor(const Vec3f & c)2754 void SolarSystem::setUranusOrbitColor(const Vec3f &c)
2755 {
2756 	if (c!=Planet::getUranusOrbitColor())
2757 	{
2758 		Planet::setUranusOrbitColor(c);
2759 		emit uranusOrbitColorChanged(c);
2760 	}
2761 }
2762 
getUranusOrbitColor(void) const2763 Vec3f SolarSystem::getUranusOrbitColor(void) const
2764 {
2765 	return Planet::getUranusOrbitColor();
2766 }
2767 
setNeptuneOrbitColor(const Vec3f & c)2768 void SolarSystem::setNeptuneOrbitColor(const Vec3f &c)
2769 {
2770 	if (c!=Planet::getNeptuneOrbitColor())
2771 	{
2772 		Planet::setNeptuneOrbitColor(c);
2773 		emit neptuneOrbitColorChanged(c);
2774 	}
2775 }
2776 
getNeptuneOrbitColor(void) const2777 Vec3f SolarSystem::getNeptuneOrbitColor(void) const
2778 {
2779 	return Planet::getNeptuneOrbitColor();
2780 }
2781 
2782 // Set/Get if Moon display is scaled
setFlagMoonScale(bool b)2783 void SolarSystem::setFlagMoonScale(bool b)
2784 {
2785 	if(b!=flagMoonScale)
2786 	{
2787 		if (b) getMoon()->setSphereScale(moonScale);
2788 		else getMoon()->setSphereScale(1);
2789 		flagMoonScale = b;
2790 		emit flagMoonScaleChanged(b);
2791 	}
2792 }
2793 
2794 // Set/Get Moon display scaling factor. This goes directly to the Moon object.
setMoonScale(double f)2795 void SolarSystem::setMoonScale(double f)
2796 {
2797 	if(!fuzzyEquals(moonScale, f))
2798 	{
2799 		moonScale = f;
2800 		if (flagMoonScale)
2801 			getMoon()->setSphereScale(moonScale);
2802 		emit moonScaleChanged(f);
2803 	}
2804 }
2805 
2806 // Set if minor body display is scaled. This flag will be queried by all Planet objects except for the Moon.
setFlagMinorBodyScale(bool b)2807 void SolarSystem::setFlagMinorBodyScale(bool b)
2808 {
2809 	if(b!=flagMinorBodyScale)
2810 	{
2811 		flagMinorBodyScale = b;
2812 
2813 		double newScale = b ? minorBodyScale : 1.0;
2814 		//update the bodies with the new scale
2815 		for (const auto& p : qAsConst(systemPlanets))
2816 		{
2817 			if(p == moon) continue;
2818 			if (p->getPlanetType()!=Planet::isPlanet && p->getPlanetType()!=Planet::isStar)
2819 				p->setSphereScale(newScale);
2820 		}
2821 		emit flagMinorBodyScaleChanged(b);
2822 	}
2823 }
2824 
2825 // Set minor body display scaling factor. This will be queried by all Planet objects except for the Moon.
setMinorBodyScale(double f)2826 void SolarSystem::setMinorBodyScale(double f)
2827 {
2828 	if(!fuzzyEquals(minorBodyScale, f))
2829 	{
2830 		minorBodyScale = f;
2831 		if(flagMinorBodyScale) //update the bodies with the new scale
2832 		{
2833 			for (const auto& p : qAsConst(systemPlanets))
2834 			{
2835 				if(p == moon) continue;
2836 				if (p->getPlanetType()!=Planet::isPlanet && p->getPlanetType()!=Planet::isStar)
2837 					p->setSphereScale(minorBodyScale);
2838 			}
2839 		}
2840 		emit minorBodyScaleChanged(f);
2841 	}
2842 }
2843 
2844 // Set if Planet display is scaled
setFlagPlanetScale(bool b)2845 void SolarSystem::setFlagPlanetScale(bool b)
2846 {
2847 	if(b!=flagPlanetScale)
2848 	{
2849 		double scale=(b ? planetScale : 1.);
2850 		for (auto& p : systemPlanets)
2851 		{
2852 			if (p->pType==Planet::isPlanet)
2853 				p->setSphereScale(scale);
2854 		}
2855 		flagPlanetScale = b;
2856 		emit flagPlanetScaleChanged(b);
2857 	}
2858 }
2859 
2860 // Set Moon display scaling factor.
setPlanetScale(double f)2861 void SolarSystem::setPlanetScale(double f)
2862 {
2863 	if(!fuzzyEquals(planetScale, f))
2864 	{
2865 		planetScale = f;
2866 		if (flagPlanetScale)
2867 			for (auto& p : systemPlanets)
2868 			{
2869 				if (p->pType==Planet::isPlanet)
2870 					p->setSphereScale(planetScale);
2871 			}
2872 		emit planetScaleChanged(f);
2873 	}
2874 }
2875 
2876 // Set if Sun display is scaled
setFlagSunScale(bool b)2877 void SolarSystem::setFlagSunScale(bool b)
2878 {
2879 	if(b!=flagSunScale)
2880 	{
2881 		if (b) getSun()->setSphereScale(sunScale);
2882 		else getSun()->setSphereScale(1);
2883 		flagSunScale = b;
2884 		emit flagSunScaleChanged(b);
2885 	}
2886 }
2887 
2888 // Set Sun display scaling factor. This goes directly to the Sun object.
setSunScale(double f)2889 void SolarSystem::setSunScale(double f)
2890 {
2891 	if(!fuzzyEquals(sunScale, f))
2892 	{
2893 		sunScale = f;
2894 		if (flagSunScale)
2895 			getSun()->setSphereScale(sunScale);
2896 		emit sunScaleChanged(f);
2897 	}
2898 }
2899 
2900 // Set selected planets by englishName
setSelected(const QString & englishName)2901 void SolarSystem::setSelected(const QString& englishName)
2902 {
2903 	setSelected(searchByEnglishName(englishName));
2904 }
2905 
2906 // Get the list of all the planet english names
getAllPlanetEnglishNames() const2907 QStringList SolarSystem::getAllPlanetEnglishNames() const
2908 {
2909 	QStringList res;
2910 	for (const auto& p : systemPlanets)
2911 		res.append(p->getEnglishName());
2912 	return res;
2913 }
2914 
getAllPlanetLocalizedNames() const2915 QStringList SolarSystem::getAllPlanetLocalizedNames() const
2916 {
2917 	QStringList res;
2918 	for (const auto& p : systemPlanets)
2919 		res.append(p->getNameI18n());
2920 	return res;
2921 }
2922 
getAllMinorPlanetCommonEnglishNames() const2923 QStringList SolarSystem::getAllMinorPlanetCommonEnglishNames() const
2924 {
2925 	QStringList res;
2926 	for (const auto& p : systemMinorBodies)
2927 		res.append(p->getCommonEnglishName());
2928 	return res;
2929 }
2930 
2931 
2932 // GZ TODO: This could be modified to only delete&reload the minor objects. For now, we really load both parts again like in the 0.10?-0.15 series.
reloadPlanets()2933 void SolarSystem::reloadPlanets()
2934 {
2935 	// Save flag states
2936 	const bool flagScaleMoon = getFlagMoonScale();
2937 	const double moonScale = getMoonScale();
2938 	const bool flagScaleMinorBodies=getFlagMinorBodyScale();
2939 	const double minorScale= getMinorBodyScale();
2940 	const bool flagPlanets = getFlagPlanets();
2941 	const bool flagHints = getFlagHints();
2942 	const bool flagLabels = getFlagLabels();
2943 	const bool flagOrbits = getFlagOrbits();
2944 	const bool flagNative = getFlagNativePlanetNames();
2945 	bool hasSelection = false;
2946 
2947 	// Save observer location (fix for LP bug # 969211)
2948 	// TODO: This can probably be done better with a better understanding of StelObserver --BM
2949 	StelCore* core = StelApp::getInstance().getCore();
2950 	const StelLocation loc = core->getCurrentLocation();
2951 	StelObjectMgr* objMgr = GETSTELMODULE(StelObjectMgr);
2952 
2953 	// Whether any planet are selected? Save the current selection...
2954 	const QList<StelObjectP> selectedObject = objMgr->getSelectedObject("Planet");
2955 	if (!selectedObject.isEmpty())
2956 	{
2957 		// ... unselect current planet.
2958 		hasSelection = true;
2959 		objMgr->unSelect();
2960 	}
2961 	// Unload all Solar System objects
2962 	selected.clear();//Release the selected one
2963 
2964 	// GZ TODO in case this methods gets converted to only reload minor bodies: Only delete Orbits which are not referenced by some Planet.
2965 	for (auto* orb : qAsConst(orbits))
2966 	{
2967 		delete orb;
2968 	}
2969 	orbits.clear();
2970 
2971 	sun.clear();
2972 	moon.clear();
2973 	earth.clear();
2974 	Planet::texEarthShadow.clear(); //Loaded in loadPlanets()
2975 
2976 	delete allTrails;
2977 	allTrails = Q_NULLPTR;
2978 
2979 	for (const auto& p : qAsConst(systemPlanets))
2980 	{
2981 		p->satellites.clear();
2982 	}
2983 	systemPlanets.clear();
2984 	systemMinorBodies.clear();
2985 	// Memory leak? What's the proper way of cleaning shared pointers?
2986 
2987 	// Also delete Comet textures (loaded in loadPlanets()
2988 	Comet::tailTexture.clear();
2989 	Comet::comaTexture.clear();
2990 
2991 	// Re-load the ssystem_major.ini and ssystem_minor.ini file
2992 	loadPlanets();
2993 	computePositions(core->getJDE(), getSun());
2994 	setSelected("");
2995 	recreateTrails();
2996 
2997 	// Restore observer location
2998 	core->moveObserverTo(loc, 0., 0.);
2999 
3000 	// Restore flag states
3001 	setFlagMoonScale(flagScaleMoon);
3002 	setMoonScale(moonScale);
3003 	setFlagMinorBodyScale(flagScaleMinorBodies);
3004 	setMinorBodyScale(1.0); // force-reset first to really reach the objects in the next call.
3005 	setMinorBodyScale(minorScale);
3006 	setFlagPlanets(flagPlanets);
3007 	setFlagHints(flagHints);
3008 	setFlagLabels(flagLabels);
3009 	setFlagOrbits(flagOrbits);
3010 	setFlagNativePlanetNames(flagNative);
3011 
3012 	// Restore translations
3013 	updateI18n();
3014 
3015 	if (hasSelection)
3016 	{
3017 		// Restore selection...
3018 		StelObjectP obj = selectedObject[0];
3019 		objMgr->findAndSelect(obj->getEnglishName(), obj->getType());
3020 	}
3021 
3022 	emit solarSystemDataReloaded();
3023 }
3024 
3025 // Set the algorithm for computation of apparent magnitudes for planets in case  observer on the Earth
setApparentMagnitudeAlgorithmOnEarth(QString algorithm)3026 void SolarSystem::setApparentMagnitudeAlgorithmOnEarth(QString algorithm)
3027 {
3028 	Planet::setApparentMagnitudeAlgorithm(algorithm);
3029 	emit apparentMagnitudeAlgorithmOnEarthChanged(algorithm);
3030 }
3031 
3032 // Get the algorithm used for computation of apparent magnitudes for planets in case  observer on the Earth
getApparentMagnitudeAlgorithmOnEarth() const3033 QString SolarSystem::getApparentMagnitudeAlgorithmOnEarth() const
3034 {
3035 	return Planet::getApparentMagnitudeAlgorithmString();
3036 }
3037 
setFlagDrawMoonHalo(bool b)3038 void SolarSystem::setFlagDrawMoonHalo(bool b)
3039 {
3040 	Planet::drawMoonHalo=b;
3041 	emit flagDrawMoonHaloChanged(b);
3042 }
3043 
getFlagDrawMoonHalo() const3044 bool SolarSystem::getFlagDrawMoonHalo() const
3045 {
3046 	return Planet::drawMoonHalo;
3047 }
3048 
setFlagDrawSunHalo(bool b)3049 void SolarSystem::setFlagDrawSunHalo(bool b)
3050 {
3051 	Planet::drawSunHalo=b;
3052 	emit flagDrawSunHaloChanged(b);
3053 }
3054 
getFlagDrawSunHalo() const3055 bool SolarSystem::getFlagDrawSunHalo() const
3056 {
3057 	return Planet::drawSunHalo;
3058 }
3059 
setFlagPermanentOrbits(bool b)3060 void SolarSystem::setFlagPermanentOrbits(bool b)
3061 {
3062 	Planet::permanentDrawingOrbits=b;
3063 	emit flagPermanentOrbitsChanged(b);
3064 }
3065 
getFlagPermanentOrbits() const3066 bool SolarSystem::getFlagPermanentOrbits() const
3067 {
3068 	return Planet::permanentDrawingOrbits;
3069 }
3070 
setOrbitsThickness(int v)3071 void SolarSystem::setOrbitsThickness(int v)
3072 {
3073 	Planet::orbitsThickness=v;
3074 	emit orbitsThicknessChanged(v);
3075 }
3076 
getOrbitsThickness() const3077 int SolarSystem::getOrbitsThickness() const
3078 {
3079 	return Planet::orbitsThickness;
3080 }
3081 
3082 
setFlagCustomGrsSettings(bool b)3083 void SolarSystem::setFlagCustomGrsSettings(bool b)
3084 {
3085 	RotationElements::flagCustomGrsSettings=b;
3086 	// automatic saving of the setting
3087 	conf->setValue("astro/flag_grs_custom", b);
3088 	emit flagCustomGrsSettingsChanged(b);
3089 }
3090 
getFlagCustomGrsSettings() const3091 bool SolarSystem::getFlagCustomGrsSettings() const
3092 {
3093 	return RotationElements::flagCustomGrsSettings;
3094 }
3095 
setCustomGrsLongitude(int longitude)3096 void SolarSystem::setCustomGrsLongitude(int longitude)
3097 {
3098 	RotationElements::customGrsLongitude = longitude;
3099 	// automatic saving of the setting
3100 	conf->setValue("astro/grs_longitude", longitude);
3101 	emit customGrsLongitudeChanged(longitude);
3102 }
3103 
getCustomGrsLongitude() const3104 int SolarSystem::getCustomGrsLongitude() const
3105 {
3106 	return RotationElements::customGrsLongitude;
3107 }
3108 
setCustomGrsDrift(double drift)3109 void SolarSystem::setCustomGrsDrift(double drift)
3110 {
3111 	RotationElements::customGrsDrift = drift;
3112 	// automatic saving of the setting
3113 	conf->setValue("astro/grs_drift", drift);
3114 	emit customGrsDriftChanged(drift);
3115 }
3116 
getCustomGrsDrift() const3117 double SolarSystem::getCustomGrsDrift() const
3118 {
3119 	return RotationElements::customGrsDrift;
3120 }
3121 
setCustomGrsJD(double JD)3122 void SolarSystem::setCustomGrsJD(double JD)
3123 {
3124 	RotationElements::customGrsJD = JD;
3125 	// automatic saving of the setting
3126 	conf->setValue("astro/grs_jd", JD);
3127 	emit customGrsJDChanged(JD);
3128 }
3129 
getCustomGrsJD()3130 double SolarSystem::getCustomGrsJD()
3131 {
3132 	return RotationElements::customGrsJD;
3133 }
3134 
setFlagEarthShadowEnlargementDanjon(bool b)3135 void SolarSystem::setFlagEarthShadowEnlargementDanjon(bool b)
3136 {
3137 	earthShadowEnlargementDanjon=b;
3138 	emit earthShadowEnlargementDanjonChanged(b);
3139 }
3140 
getFlagEarthShadowEnlargementDanjon() const3141 bool SolarSystem::getFlagEarthShadowEnlargementDanjon() const
3142 {
3143 	return earthShadowEnlargementDanjon;
3144 }
3145 
setOrbitColorStyle(QString style)3146 void SolarSystem::setOrbitColorStyle(QString style)
3147 {
3148 	if (style.toLower()=="groups")
3149 		Planet::orbitColorStyle = Planet::ocsGroups;
3150 	else if (style.toLower()=="major_planets")
3151 		Planet::orbitColorStyle = Planet::ocsMajorPlanets;
3152 	else
3153 		Planet::orbitColorStyle = Planet::ocsOneColor;
3154 }
3155 
getOrbitColorStyle() const3156 QString SolarSystem::getOrbitColorStyle() const
3157 {
3158 	QString r = "one_color";
3159 	switch (Planet::orbitColorStyle)
3160 	{
3161 		case Planet::ocsOneColor:
3162 			r = "one_color";
3163 			break;
3164 		case Planet::ocsGroups:
3165 			r = "groups";
3166 			break;
3167 		case Planet::ocsMajorPlanets:
3168 			r = "major_planets";
3169 			break;
3170 	}
3171 	return r;
3172 }
3173 
3174 // TODO: To make the code better understandable, get rid of planet->computeModelMatrix(trans, true) here.
getSolarEclipseFactor(const StelCore * core) const3175 QPair<double, PlanetP> SolarSystem::getSolarEclipseFactor(const StelCore* core) const
3176 {
3177 	PlanetP p;
3178 	const Vec3d Lp = sun->getEclipticPos() + sun->getAberrationPush();
3179 	const Vec3d P3 = core->getObserverHeliocentricEclipticPos();
3180 	const double RS = sun->getEquatorialRadius();
3181 
3182 	double final_illumination = 1.0;
3183 
3184 	for (const auto& planet : systemPlanets)
3185 	{
3186 		if(planet == sun || planet == core->getCurrentPlanet())
3187 			continue;
3188 
3189 		Mat4d trans;
3190 		planet->computeModelMatrix(trans, true);
3191 
3192 		const Vec3d C = trans * Vec3d(0., 0., 0.);
3193 		const double radius = planet->getEquatorialRadius();
3194 
3195 		Vec3d v1 = Lp - P3;
3196 		Vec3d v2 = C - P3;
3197 		const double L = v1.length();
3198 		const double l = v2.length();
3199 		v1 /= L;
3200 		v2 /= l;
3201 
3202 		const double R = RS / L;
3203 		const double r = radius / l;
3204 		const double d = ( v1 - v2 ).length();
3205 		double illumination;
3206 
3207 		if(d >= R + r) // distance too far
3208 		{
3209 			illumination = 1.0;
3210 		}
3211 		else if(d <= r - R) // umbra
3212 		{
3213 			illumination = 0.0;
3214 		}
3215 		else if(d <= R - r) // penumbra completely inside
3216 		{
3217 			illumination = 1.0 - r * r / (R * R);
3218 		}
3219 		else // penumbra partially inside
3220 		{
3221 			const double x = (R * R + d * d - r * r) / (2.0 * d);
3222 
3223 			const double alpha = std::acos(x / R);
3224 			const double beta = std::acos((d - x) / r);
3225 
3226 			const double AR = R * R * (alpha - 0.5 * std::sin(2.0 * alpha));
3227 			const double Ar = r * r * (beta - 0.5 * std::sin(2.0 * beta));
3228 			const double AS = R * R * 2.0 * std::asin(1.0);
3229 
3230 			illumination = 1.0 - (AR + Ar) / AS;
3231 		}
3232 
3233 		if(illumination < final_illumination)
3234 		{
3235 			final_illumination = illumination;
3236 			p = planet;
3237 		}
3238 	}
3239 
3240 	return QPair<double, PlanetP>(final_illumination, p);
3241 }
3242 
3243 // Retrieve Radius of Umbra and Penumbra at the distance of the Moon.
3244 // Returns a pair (umbra, penumbra) in (geocentric_arcseconds, AU, geometric_AU).
3245 // * sizes in arcseconds are the usual result found as Bessel element in eclipse literature.
3246 //   It includes scaling for effects of atmosphere either after Chauvenet (2%) or after Danjon. (see Espenak: 5000 Years Canon of Lunar Eclipses.)
3247 // * sizes in AU are the same, converted back to AU in Lunar distance.
3248 // * sizes in geometric_AU derived from pure geometrical evaluations without scalings applied.
getEarthShadowRadiiAtLunarDistance() const3249 QPair<Vec3d,Vec3d> SolarSystem::getEarthShadowRadiiAtLunarDistance() const
3250 {
3251 	// Note: The application of this shadow enlargement is not according to the books, but looks close enough for now.
3252 	static const double sun2earth=sun->getEquatorialRadius() / earth->getEquatorialRadius();
3253 	PlanetP sun=getSun();
3254 	PlanetP moon=getMoon();
3255 	PlanetP earth=getEarth();
3256 	const double lunarDistance=moon->getEclipticPos().length(); // Lunar distance [AU]
3257 	const double earthDistance=earth->getHeliocentricEclipticPos().length(); // Earth distance [AU]
3258 	const double sunHP =asin(earth->getEquatorialRadius()/earthDistance) * M_180_PI*3600.; // arcsec.
3259 	const double moonHP=asin(earth->getEquatorialRadius()/lunarDistance) * M_180_PI*3600.; // arcsec.
3260 	const double sunSD  =atan(sun->getEquatorialRadius()/earthDistance)  * M_180_PI*3600.; // arcsec.
3261 
3262 	// Compute umbra radius at lunar distance.
3263 	const double lUmbra=earthDistance/(sun2earth-1.); // length of earth umbra [AU]
3264 	const double rUmbraAU=earth->getEquatorialRadius()*(lUmbra-lunarDistance)/lUmbra; // radius of earth shadow at lunar distance [AU]
3265 	// Penumbra:
3266 	const double lPenumbra=earthDistance/(sun2earth + 1.); // distance between earth and point between sun and earth where penumbral border rays intersect
3267 	const double rPenumbraAU=earth->getEquatorialRadius()*(lPenumbra+lunarDistance)/lPenumbra; // radius of penumbra at Lunar distance [AU]
3268 
3269 	//Classical Bessel elements instead
3270 	double f1, f2;
3271 	if (earthShadowEnlargementDanjon)
3272 	{
3273 		static const double danjonScale=1+1./85.-1./594.; // ~1.01, shadow magnification factor (see Espenak 5000 years Canon)
3274 		f1=danjonScale*moonHP + sunHP + sunSD; // penumbra radius, arcsec
3275 		f2=danjonScale*moonHP + sunHP - sunSD; // umbra radius, arcsec
3276 	}
3277 	else
3278 	{
3279 		const double mHP1=0.998340*moonHP;
3280 		f1=1.02*(mHP1 + sunHP + sunSD); // penumbra radius, arcsec
3281 		f2=1.02*(mHP1 + sunHP - sunSD); // umbra radius, arcsec
3282 	}
3283 	const double f1_AU=tan(f1/3600.*M_PI_180)*lunarDistance;
3284 	const double f2_AU=tan(f2/3600.*M_PI_180)*lunarDistance;
3285 	return QPair<Vec3d,Vec3d>(Vec3d(f2, f2_AU, rUmbraAU), Vec3d(f1, f1_AU, rPenumbraAU));
3286 }
3287 
removeMinorPlanet(QString name)3288 bool SolarSystem::removeMinorPlanet(QString name)
3289 {
3290 	PlanetP candidate = searchMinorPlanetByEnglishName(name);
3291 	if (!candidate)
3292 	{
3293 		qWarning() << "Cannot remove planet " << name << ": Not found.";
3294 		return false;
3295 	}
3296 	Orbit* orbPtr=static_cast<Orbit*>(candidate->orbitPtr);
3297 	if (orbPtr)
3298 		orbits.removeOne(orbPtr);
3299 	systemPlanets.removeOne(candidate);
3300 	systemMinorBodies.removeOne(candidate);
3301 	candidate.clear();
3302 	return true;
3303 }
3304 
onNewSurvey(HipsSurveyP survey)3305 void SolarSystem::onNewSurvey(HipsSurveyP survey)
3306 {
3307 	// For the moment we only consider the survey url to decide if we
3308 	// assign it to a planet.  It would be better to use some property
3309 	// for that.
3310 	QString planetName = QUrl(survey->getUrl()).fileName();
3311 	PlanetP pl = searchByEnglishName(planetName);
3312 	if (!pl || pl->survey)
3313 		return;
3314 	pl->survey = survey;
3315 	survey->setProperty("planet", pl->getCommonEnglishName());
3316 	// Not visible by default for the moment.
3317 	survey->setProperty("visible", false);
3318 }
3319