Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -1,5 +1,4 @@ PROJECT(kstars CXX C) - set (KStars_VERSION_MAJOR 3) set (KStars_VERSION_MINOR 0) set (KStars_VERSION_REVISION 0) @@ -7,6 +6,7 @@ # Build KStars Lite with -DBUILD_KSTARS_LITE=ON option(BUILD_KSTARS_LITE "Build KStars Lite" OFF) + # Fetch translations with -DFETCH_TRANSLATIONS=ON option(FETCH_TRANSLATIONS "Fetch Translations" OFF) Index: kstars/CMakeLists.txt =================================================================== --- kstars/CMakeLists.txt +++ kstars/CMakeLists.txt @@ -303,7 +303,13 @@ tools/avtplotwidget.cpp tools/calendarwidget.cpp tools/conjunctions.cpp + tools/eclipsetool.cpp + tools/eclipsehandler.cpp + + tools/eclipsetool/lunareclipsehandler.cpp + # tools/jmoontool.cpp + tools/approachsolver.cpp tools/ksconjunct.cpp tools/eqplotwidget.cpp tools/astrocalc.cpp @@ -377,6 +383,7 @@ tools/argwaitforkey.ui tools/argzoom.ui tools/conjunctions.ui + tools/eclipsetool.ui tools/modcalcangdist.ui tools/modcalcapcoord.ui @@ -568,6 +575,7 @@ skycomponents/pointlistcomponent.cpp skycomponents/solarsystemsinglecomponent.cpp skycomponents/solarsystemlistcomponent.cpp + skycomponents/earthshadowcomponent.cpp skycomponents/asteroidscomponent.cpp skycomponents/cometscomponent.cpp skycomponents/planetmoonscomponent.cpp @@ -621,6 +629,7 @@ skyobjects/ksasteroid.cpp skyobjects/kscomet.cpp skyobjects/ksmoon.cpp + skyobjects/ksearthshadow.cpp skyobjects/ksplanetbase.cpp skyobjects/ksplanet.cpp #skyobjects/kspluto.cpp Index: kstars/kstarsdata.cpp =================================================================== --- kstars/kstarsdata.cpp +++ kstars/kstarsdata.cpp @@ -291,7 +291,7 @@ LastSkyUpdate = ut(); m_preUpdateID++; //omit KSNumbers arg == just update Alt/Az coords // <-- Eh? -- asimha. Looks like this behavior / ideology has changed drastically. - skyComposite()->update(); + skyComposite()->update(&num); emit skyUpdate(clock()->isManualMode()); } Index: kstars/skycomponents/binarylistcomponent.h =================================================================== --- kstars/skycomponents/binarylistcomponent.h +++ kstars/skycomponents/binarylistcomponent.h @@ -23,24 +23,24 @@ #include "binarylistcomponent.h" #include "auxiliary/kspaths.h" -//TODO Error Handling +//TODO: Error Handling - SERIOUSLY /** - *@class BinaryListComponent - *@short provides functionality for loading the component data from Binary - *@author Valentin Boettcher - *@version 1.0 + * @class BinaryListComponent + * @short provides functionality for loading the component data from Binary + * @author Valentin Boettcher + * @version 1.0 * * This class is an abstract Template which requires that the type `T` is some child of * `SkyObject` and the type `Component` is some child of `ListComponent`. The class `T` * must provide a static `TYPE` property of the type `SkyObject::TYPE`. This is required - * because access to the `type()` method are inconvenient here! + * because access to the `type()` method is inconvenient here! * * The derived class must provide a `void loadFromText()` method, which loads the component * via `addListObject` or similar. (This method implements parsing etc, and cannot be * abstracted by this class.) * - * To make this template work, one has to add it as a friend class upon deriving it. + * Finally, one has to add this template as a friend class upon deriving it. * This is a concession to the already present architecture. * * File paths are determent by the means of KSPaths::writableLocation. Index: kstars/skycomponents/earthshadowcomponent.h =================================================================== --- /dev/null +++ kstars/skycomponents/earthshadowcomponent.h @@ -0,0 +1,43 @@ +/*************************************************************************** + ksearthshadow.h - K Desktop Planetarium + ------------------- + begin : Fri Aug 24 2018 + copyright : (C) 2018 by Valentin Boettcher + email : valentin@boettcher.cf (do not hesitate to contact) + matrix : @hiro98@tchncs.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#pragma once +#include "skycomponent.h" +#include "ksearthshadow.h" // in here for inline definitions + +class SkyComposite; +class SkyPainter; + +/** + * @brief The EarthShadowComponent class + * @short A simple skycomponent for the KSEarthShadow. + */ +class EarthShadowComponent : public SkyComponent +{ +public: + EarthShadowComponent(SkyComposite * parent, KSEarthShadow * shadow); + + void update(KSNumbers *num) override; + void updateSolarSystemBodies(KSNumbers *num) override; + bool selected() override { return m_shadow->shouldUpdate(); } + void draw(SkyPainter *skyp) override; + +private: + KSEarthShadow * m_shadow; + bool m_up_to_date; +}; Index: kstars/skycomponents/earthshadowcomponent.cpp =================================================================== --- /dev/null +++ kstars/skycomponents/earthshadowcomponent.cpp @@ -0,0 +1,59 @@ +/*************************************************************************** + ksearthshadow.cpp - K Desktop Planetarium + ------------------- + begin : Fri Aug 24 2018 + copyright : (C) 2018 by Valentin Boettcher + email : valentin@boettcher.cf (do not hesitate to contact) + matrix : @hiro98@tchncs.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "earthshadowcomponent.h" +#include "skycomposite.h" +#include "skypainter.h" +#include "kstarsdata.h" + +//TODO: (Valentin) Error Handling +EarthShadowComponent::EarthShadowComponent(SkyComposite *parent, KSEarthShadow *shadow) : SkyComponent(parent) +{ + m_shadow = shadow; + m_up_to_date = false; +} + +void EarthShadowComponent::update(KSNumbers *) +{ + KStarsData *data = KStarsData::Instance(); + m_shadow->EquatorialToHorizontal(data->lst(), data->geo()->lat()); +} + +void EarthShadowComponent::updateSolarSystemBodies(KSNumbers *num) +{ + // don't bother if the moon is not in position + if(!m_shadow->shouldUpdate()) { // TODO: There must be sth. more efficient + m_up_to_date = false; + return; + } + + KStarsData *data = KStarsData::Instance(); + CachingDms *LST = data->lst(); + const CachingDms *lat = data->geo()->lat(); + + m_shadow->findPosition(num, lat, LST); + m_shadow->EquatorialToHorizontal(LST, lat); + m_up_to_date = true; +} + +void EarthShadowComponent::draw(SkyPainter *skyp) +{ + // check if the shadow has been updated + if(m_up_to_date && (m_shadow->isInEclipse())) + skyp->drawEarthShadow(m_shadow); +} Index: kstars/skycomponents/skycomponent.h =================================================================== --- kstars/skycomponents/skycomponent.h +++ kstars/skycomponents/skycomponent.h @@ -59,6 +59,7 @@ * (directly or indirectly). Its most important derivative is SkyComposite, the base classes * for all Composites. * + * FIXME: There is no SingleComponent * From SkyComponent, we derive three important subclasses: SingleComponent, ListComponent, * and PointListComponent. SingleComponent represents a sky element consisting of a single * SkyObject, such as the Sun. ListComponent represents a list of SkyObjects, such as the Index: kstars/skycomponents/skymapcomposite.h =================================================================== --- kstars/skycomponents/skymapcomposite.h +++ kstars/skycomponents/skymapcomposite.h @@ -114,7 +114,7 @@ * @sa updatePlanets() * @sa SolarSystemComposite::updateMoons() */ - // virtual void updateMoons( KSNumbers *num ); + void updateMoons( KSNumbers *num ) override; /** * @short Delegate draw requests to all sub components Index: kstars/skycomponents/skymapcomposite.cpp =================================================================== --- kstars/skycomponents/skymapcomposite.cpp +++ kstars/skycomponents/skymapcomposite.cpp @@ -233,12 +233,11 @@ m_SolarSystem->updateSolarSystemBodies(num); } -/* + void SkyMapComposite::updateMoons(KSNumbers *num ) { m_SolarSystem->updateMoons( num ); } -*/ //Reimplement draw function so that we have control over the order of //elements, and we can add object labels Index: kstars/skycomponents/solarsystemcomposite.h =================================================================== --- kstars/skycomponents/solarsystemcomposite.h +++ kstars/skycomponents/solarsystemcomposite.h @@ -27,7 +27,7 @@ class KSSun; //class JupiterMoonsComponent; class SkyLabeler; - +class KSEarthShadow; /** * @class SolarSystemComposite * The solar system composite manages all planets, asteroids and comets. @@ -44,9 +44,11 @@ explicit SolarSystemComposite(SkyComposite *parent); ~SolarSystemComposite() override; + // Use this instead of `findByName` KSPlanet *earth() { return m_Earth; } - KSSun *sun() { return m_Sun; } + KSMoon *moon() { return m_Moon; } + KSEarthShadow *earthShadow() { return m_EarthShadow; } const QList &asteroids() const; const QList &comets() const; @@ -75,6 +77,8 @@ KSPlanet *m_Earth { nullptr }; KSSun *m_Sun { nullptr }; KSMoon *m_Moon { nullptr }; + KSEarthShadow *m_EarthShadow { nullptr }; + // PlanetMoonsComponent *m_JupiterMoons; AsteroidsComponent *m_AsteroidsComponent; CometsComponent *m_CometsComponent; Index: kstars/skycomponents/solarsystemcomposite.cpp =================================================================== --- kstars/skycomponents/solarsystemcomposite.cpp +++ kstars/skycomponents/solarsystemcomposite.cpp @@ -25,21 +25,25 @@ #include "skymap.h" #endif #include "solarsystemsinglecomponent.h" +#include "earthshadowcomponent.h" #include "skyobjects/ksmoon.h" #include "skyobjects/ksplanet.h" #include "skyobjects/kssun.h" +#include "skyobjects/ksearthshadow.h" SolarSystemComposite::SolarSystemComposite(SkyComposite *parent) : SkyComposite(parent) { emitProgressText(i18n("Loading solar system")); m_Earth = new KSPlanet(I18N_NOOP("Earth"), QString(), QColor("white"), 12756.28 /*diameter in km*/); - m_Sun = new KSSun(); SolarSystemSingleComponent *sun = new SolarSystemSingleComponent(this, m_Sun, Options::showSun); addComponent(sun, 2); m_Moon = new KSMoon(); - SolarSystemSingleComponent *moon = new SolarSystemSingleComponent(this, m_Moon, Options::showMoon); + SolarSystemSingleComponent *moon = new SolarSystemSingleComponent(this, m_Moon, Options::showMoon, true); addComponent(moon, 3); + m_EarthShadow = new KSEarthShadow(m_Moon, m_Sun, m_Earth); + EarthShadowComponent * shadow = new EarthShadowComponent(this, m_EarthShadow); + addComponent(shadow); SolarSystemSingleComponent *mercury = new SolarSystemSingleComponent(this, new KSPlanet(KSPlanetBase::MERCURY), Options::showMercury); addComponent(mercury, 4); @@ -101,7 +105,6 @@ SolarSystemComposite::~SolarSystemComposite() { - delete m_Earth; } bool SolarSystemComposite::selected() @@ -140,10 +143,11 @@ void SolarSystemComposite::updateMoons(KSNumbers *num) { // if ( ! selected() ) return; - KStarsData *data = KStarsData::Instance(); - m_Sun->findPosition(num); - m_Moon->findPosition(num, data->geo()->lat(), data->lst()); - m_Moon->findPhase(nullptr); + m_Earth->findPosition(num); + foreach (SkyComponent *comp, components()) + { + comp->updateMoons(num); + } // m_JupiterMoons->updateMoons( num ); } Index: kstars/skycomponents/solarsystemsinglecomponent.h =================================================================== --- kstars/skycomponents/solarsystemsinglecomponent.h +++ kstars/skycomponents/solarsystemsinglecomponent.h @@ -41,7 +41,7 @@ { public: /** Initialize visible method, minimum size and sizeScale. */ - SolarSystemSingleComponent(SolarSystemComposite *, KSPlanetBase *kspb, bool (*visibleMethod)()); + SolarSystemSingleComponent(SolarSystemComposite *, KSPlanetBase *kspb, bool (*visibleMethod)(), bool isMoon = false); ~SolarSystemSingleComponent() override; @@ -62,6 +62,12 @@ */ void updateSolarSystemBodies(KSNumbers *num) override; + /** + * @brief update Update Equtorial & Horizontal coordinates. (Called more frequently.) + */ + void updateMoons(KSNumbers *num) override; + + SkyObject *findByName(const QString &name) override; SkyObject *objectNearest(SkyPoint *p, double &maxrad) override; void draw(SkyPainter *skyp) override; @@ -71,6 +77,7 @@ private: bool (*visible)(); + bool m_isMoon { false }; QColor m_Color; KSPlanet *m_Earth; KSPlanetBase *m_Planet; Index: kstars/skycomponents/solarsystemsinglecomponent.cpp =================================================================== --- kstars/skycomponents/solarsystemsinglecomponent.cpp +++ kstars/skycomponents/solarsystemsinglecomponent.cpp @@ -37,8 +37,8 @@ #include "projections/projector.h" SolarSystemSingleComponent::SolarSystemSingleComponent(SolarSystemComposite *parent, KSPlanetBase *kspb, - bool (*visibleMethod)()) - : SkyComponent(parent), visible(visibleMethod), m_Earth(parent->earth()), m_Planet(kspb) + bool (*visibleMethod)(), bool isMoon) + : SkyComponent(parent), visible(visibleMethod), m_isMoon(isMoon), m_Earth(parent->earth()), m_Planet(kspb) { m_Planet->loadData(); if (!m_Planet->name().isEmpty()) @@ -94,7 +94,7 @@ void SolarSystemSingleComponent::updateSolarSystemBodies(KSNumbers *num) { - if (selected()) + if (!m_isMoon && selected()) { KStarsData *data = KStarsData::Instance(); m_Planet->findPosition(num, data->geo()->lat(), data->lst(), m_Earth); @@ -104,6 +104,18 @@ } } +// NOTE: This seems like code duplication, and yes IT IS. But there may be some +// NOTE: changes to be made to it later on, and calling `updateSolarSystemBodies` +// NOTE: is ugly. +void SolarSystemSingleComponent::updateMoons(KSNumbers *num) +{ + KStarsData *data = KStarsData::Instance(); + m_Planet->findPosition(num, data->geo()->lat(), data->lst(), m_Earth); + m_Planet->EquatorialToHorizontal(data->lst(), data->geo()->lat()); + if (m_Planet->hasTrail()) + m_Planet->updateTrail(data->lst(), data->geo()->lat()); +} + void SolarSystemSingleComponent::draw(SkyPainter *skyp) { if (!selected()) Index: kstars/skycomponents/typedef.h =================================================================== --- kstars/skycomponents/typedef.h +++ kstars/skycomponents/typedef.h @@ -23,6 +23,15 @@ * some of the surprise. * * -- James B. Bowlin + * + * We may transition to the use of smart pointers whereever possible. Gathering + * syntactic sugar here, takes the pain of it away. + * + * Conventions: + * - [x]_ptr => Type_[first letter of `x`] + * for example: typedef std::shared_ptr SkyObject_s; + * + * -- Valentin Boettcher */ #pragma once @@ -38,6 +47,9 @@ class StarObject; class StarBlock; class SkyObject; +class KSPlanetBase; +class GeoLocation; +class EclipseEvent; typedef quint32 DrawID; typedef quint32 UpdateID; @@ -52,3 +64,7 @@ typedef QVector> LineListList; typedef QHash> LineListHash; typedef QList SkyObjectList; + +typedef std::shared_ptr SkyObject_s; +typedef std::shared_ptr KSPlanetBase_s; +typedef std::shared_ptr EclipseEvent_s; Index: kstars/skyobjects/ksearthshadow.h =================================================================== --- /dev/null +++ kstars/skyobjects/ksearthshadow.h @@ -0,0 +1,131 @@ +/*************************************************************************** + ksearthshadow.h - K Desktop Planetarium + ------------------- + begin : Fri Aug 24 2018 + copyright : (C) 2018 by Valentin Boettcher + email : valentin@boettcher.cf (do not hesitate to contact) + matrix : @hiro98@tchncs.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#pragma once + +#include "ksplanetbase.h" + +class KSSun; +class KSMoon; +class KSPlanet; + +/** + * @class KSEarthShadow + * @short A class that manages the calulation of the + * earths shadow (in moon distance) as a 'virtual' skyobject. + * KSMoon is responsible for coordinating this object. While a + * rather unusual measure, this method ensures that unnecessary + * calculations are avoided. + * + * @author Valentin Boettcher + * @version 1.0 + */ +class KSEarthShadow : public KSPlanetBase +{ +public: + /** + * @param moon - an instance of KSMoon + * @param sun - an instance of KSSun + * @param earth - an instance of KSPlanet + * @note The three parameters must be supplied to avoid initialization order + * problems. This class may be generalized to any three bodies if it becomes + * necessary in the future. + * This class is relatively cheap, so it's save to create new instances instead + * of reusing an existing one. + */ + KSEarthShadow(const KSMoon *moon, const KSSun *sun, const KSPlanet * earth); + + /** + * @brief The ECLIPSE_TYPE enum describes the quality of an eclipse. + */ + enum ECLIPSE_TYPE { + PARTIAL, FULL_PENUMBRA, FULL_UMBRA, NONE + }; + + /** + * @short The earths shadow on the moon appears only at new moon + * so calculating it on other occasions is rather pointless. + * @return whether to update the shadow or not + */ + bool shouldUpdate(); + + + /** + * @brief isInEclipse - a slim version of getEclipseType() + * @return Whether the earth shadow eclipses the moon. + */ + bool isInEclipse(); + + /** + * @brief eclipse + * @return The eclipse type. @see KSEarthShadow::ECLIPSE_TYPE + */ + ECLIPSE_TYPE getEclipseType(); + + bool findGeocentricPosition(const KSNumbers *, const KSPlanetBase * Earth = nullptr) override; + + /** + * @short Update the Coordinates of the shadow. + * In truth it finds the sun and calls KSEarthShadow::updateCoords(const KSSun *) + */ + void updateCoords(const KSNumbers *num, bool includePlanets = true, const CachingDms *lat = nullptr, + const CachingDms *LST = nullptr, bool forceRecompute = false) override; + + /** + * @short Update the RA/DEC of the shadow. + */ + void updateCoords(); + + /** + * @short Updates umbra and penumbra radius from the positions of the three bodies. + */ + void calculateShadowRadius(); + + /** + * @return The angular radius of the umbra. + */ + double getUmbraAngSize() const { return m_umbra_ang; } + + /** + * @return The angular radius of the penumbra. + */ + double getPenumbraAngSize() const { return m_penumbra_ang; } + + /** + * @brief angSize + * @return the angular size (penumbra) in arc minutes + */ + double findAngularSize() final { calculateShadowRadius(); return m_penumbra_ang; } + + // Some Compatibility Nonsense + void findMagnitude(const KSNumbers *) override {} // Empty + bool loadData() override { return true; } + void findPhase() override {} + +private: + double m_umbra_ang { 0 }; // Radius! + double m_penumbra_ang { 0 }; // Radius! + + const KSSun* m_sun { nullptr }; + const KSMoon* m_moon { nullptr }; + const KSPlanet* m_earth { nullptr }; + + void findSun(); + void findMoon(); + void findEarth(); +}; Index: kstars/skyobjects/ksearthshadow.cpp =================================================================== --- /dev/null +++ kstars/skyobjects/ksearthshadow.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + ksearthshadow.cpp - K Desktop Planetarium + ------------------- + begin : Fri Aug 24 2018 + copyright : (C) 2018 by Valentin Boettcher + email : valentin@boettcher.cf + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include "ksearthshadow.h" +#include "kssun.h" +#include "ksmoon.h" + +KSEarthShadow::KSEarthShadow(const KSMoon *moon, const KSSun *sun, const KSPlanet * earth) + : KSPlanetBase ("Earth Shadow"), m_sun { sun }, m_moon { moon }, m_earth {earth} +{ +} + +bool KSEarthShadow::shouldUpdate() +{ + return (m_moon->illum() > 0.8); +} + +bool KSEarthShadow::isInEclipse() +{ + double dist = angularDistanceTo(m_moon).Degrees() * 60; + return (dist - m_moon->angSize() / 2) <= m_penumbra_ang; +} + +KSEarthShadow::ECLIPSE_TYPE KSEarthShadow::getEclipseType() +{ + double half_a_moon = m_moon->angSize() / 2; + double dist = angularDistanceTo(m_moon).Degrees() * 60; // arcminutes + if (dist <= (m_penumbra_ang - half_a_moon)) { + if(dist <= (m_umbra_ang - half_a_moon)) + return FULL_UMBRA; + else + return FULL_PENUMBRA; + } else if ((dist - half_a_moon) <= m_penumbra_ang) { + return PARTIAL; + } + + return NONE; +} + +bool KSEarthShadow::findGeocentricPosition(const KSNumbers *, const KSPlanetBase *) +{ + updateCoords(); + return true; //TODO: not nice! +} + + +void KSEarthShadow::updateCoords(const KSNumbers *, bool, const CachingDms *, const CachingDms *, bool ) +{ + updateCoords(); +} + +//TODO: Abort if Null +void KSEarthShadow::updateCoords() +{ + // flip the sun around to get the 'shadow coordinates' + dms t_ra(m_sun->ra().Degrees() + 180); + t_ra.reduceToRange(dms::ZERO_TO_2PI); + dms t_dec(-1*(m_sun->dec().Degrees())); + + set(t_ra, t_dec); + Rearth = m_moon->rearth(); +} + + +//NOTE: This can easily be generalized to any three bodies. +void KSEarthShadow::calculateShadowRadius() +{ + double d_sun = m_sun->rearth() * AU_KM; + double d_moon = m_moon->rearth() * AU_KM; + double r_sun = m_sun->physicalSize() / 2; + double r_earth = m_earth->physicalSize() / 2; + + double umbraRad = 1.01 * r_earth + (r_earth-r_sun)/d_sun * d_moon; + double penumbraRad = 1.01 * r_earth+(r_sun+r_earth)/d_sun*d_moon; + + m_umbra_ang = asin(umbraRad / d_moon) * 60. * 180. / dms::PI; + m_penumbra_ang = asin(penumbraRad / d_moon) * 60. * 180. / dms::PI; + + return; +} Index: kstars/skyobjects/ksmoon.h =================================================================== --- kstars/skyobjects/ksmoon.h +++ kstars/skyobjects/ksmoon.h @@ -51,10 +51,10 @@ /** * Determine the phase angle of the moon, and assign the appropriate moon image * @param Sun a KSSun pointer with coordinates updated to the time of computation. - * If not supplied, the findByName() method will be used to find the sun. + * If not supplied, the sun is retrieved via KStarsData * @note Overrides KSPlanetBase::findPhase() */ - virtual void findPhase(const KSSun *Sun = nullptr); + void findPhase(const KSSun *Sun = nullptr); /** @return the illuminated fraction of the Moon as seen from Earth */ double illum() const { return 0.5 * (1.0 - cos(Phase * dms::PI / 180.0)); } Index: kstars/skyobjects/ksmoon.cpp =================================================================== --- kstars/skyobjects/ksmoon.cpp +++ kstars/skyobjects/ksmoon.cpp @@ -25,6 +25,7 @@ #include "kspopupmenu.h" #endif #include "skycomponents/skymapcomposite.h" +#include "skycomponents/solarsystemcomposite.h" #include "texturemanager.h" #include @@ -276,12 +277,14 @@ if (Sun == nullptr) { if (defaultSun == nullptr) - defaultSun = dynamic_cast(KStarsData::Instance()->skyComposite()->findByName("Sun")); + defaultSun = KStarsData::Instance()->skyComposite()->solarSystemComposite()->sun(); Sun = defaultSun; } Q_ASSERT(Sun != nullptr); + // This is an approximation justified by the small Earth-Moon distance in relation + // to the great Earth-Sun distance Phase = (ecLong() - Sun->ecLong()).Degrees(); // Phase is obviously in degrees double DegPhase = dms(Phase).reduce().Degrees(); iPhase = int(0.1 * DegPhase + 0.5) % 36; // iPhase must be in [0,36) range Index: kstars/skyobjects/ksplanetbase.h =================================================================== --- kstars/skyobjects/ksplanetbase.h +++ kstars/skyobjects/ksplanetbase.h @@ -18,6 +18,7 @@ #pragma once #include "trailobject.h" +#include "kstarsdata.h" #include #include @@ -87,6 +88,7 @@ NEPTUNE = 6, SUN = 7, MOON = 8, + EARTH_SHADOW = 9, UNKNOWN_PLANET }; @@ -257,6 +259,7 @@ /** Determine the phase of the planet. */ virtual void findPhase(); + virtual double findAngularSize() { return asin(physicalSize() / Rearth / AU_KM) * 60. * 180. / dms::PI; } // Geocentric ecliptic position, but distance to the Sun EclipticPosition ep; Index: kstars/skyobjects/ksplanetbase.cpp =================================================================== --- kstars/skyobjects/ksplanetbase.cpp +++ kstars/skyobjects/ksplanetbase.cpp @@ -128,7 +128,7 @@ findGeocentricPosition(num, Earth); //private function, reimplemented in each subclass findPhase(); - setAngularSize(asin(physicalSize() / Rearth / AU_KM) * 60. * 180. / dms::PI); //angular size in arcmin + setAngularSize(findAngularSize()); //angular size in arcmin if (lat && LST) localizeCoords(num, lat, LST); //correct for figure-of-the-Earth Index: kstars/skyobjects/skyobject.cpp =================================================================== --- kstars/skyobjects/skyobject.cpp +++ kstars/skyobjects/skyobject.cpp @@ -303,7 +303,7 @@ * is needed. */ - if (name() == "Sun" || name() == "Moon") + if (name() == "Sun" || name() == "Moon" || name() == "Earth Shadow") return dms(-0.8333); // else if ( name() == "Moon" ) // return dms(0.125); Index: kstars/skyobjects/skypoint.h =================================================================== --- kstars/skyobjects/skypoint.h +++ kstars/skyobjects/skypoint.h @@ -646,5 +646,5 @@ static KSSun *m_Sun; protected: - double lastPrecessJD { 0 }; // JD at which the last coordinate update (see updateCoords) for this SkyPoint was done + double lastPrecessJD { 0 }; // JD at which the last coordinate (see updateCoords) for this SkyPoint was done }; Index: kstars/skyobjects/skypoint.cpp =================================================================== --- kstars/skyobjects/skypoint.cpp +++ kstars/skyobjects/skypoint.cpp @@ -91,7 +91,7 @@ 1 - sinAlt * sinAlt); // Avoid trigonometric function. Return value of asin is always in [-pi/2, pi/2] and in this domain cosine is always non-negative, so we can use this. - if (cosAlt == 0) + if (cosAlt == 0.) cosAlt = cos(AltRad); double arg = (sindec - sinlat * sinAlt) / (coslat * cosAlt); Index: kstars/skypainter.h =================================================================== --- kstars/skypainter.h +++ kstars/skypainter.h @@ -29,6 +29,7 @@ class DeepSkyObject; class KSComet; class KSPlanetBase; +class KSEarthShadow; class LineList; class LineListLabel; class Satellite; @@ -142,6 +143,13 @@ */ virtual bool drawPlanet(KSPlanetBase *planet) = 0; + /** + * @short Draw the earths shadow on the moon (red-ish) + * @param shadwo the shadow to draw + * @return true if it was drawn + */ + virtual bool drawEarthShadow(KSEarthShadow *shadow) = 0; + /** * @short Draw the symbols for the observing list * @param obs the oberving list Index: kstars/skyqpainter.h =================================================================== --- kstars/skyqpainter.h +++ kstars/skyqpainter.h @@ -29,6 +29,7 @@ class QSize; class QMessageBox; class HIPSRenderer; +class KSEarthShadow; /** * @short The QPainter-based painting backend. @@ -88,6 +89,7 @@ bool drawPointSource(SkyPoint *loc, float mag, char sp = 'A') override; bool drawDeepSkyObject(DeepSkyObject *obj, bool drawImage = false) override; bool drawPlanet(KSPlanetBase *planet) override; + bool drawEarthShadow(KSEarthShadow *shadow) override; void drawObservingList(const QList &obs) override; void drawFlags() override; void drawHorizon(bool filled, SkyPoint *labelPoint = nullptr, bool *drawLabel = nullptr) override; @@ -100,7 +102,7 @@ bool drawConstellationArtImage(ConstellationsArt *obj) override; bool drawHips() override; - private: +private: virtual bool drawDeepSkyImage(const QPointF &pos, DeepSkyObject *obj, float positionAngle); QPaintDevice *m_pd { nullptr }; Index: kstars/skyqpainter.cpp =================================================================== --- kstars/skyqpainter.cpp +++ kstars/skyqpainter.cpp @@ -32,6 +32,7 @@ #include "skycomponents/skiphashlist.h" #include "skycomponents/skymapcomposite.h" #include "skycomponents/solarsystemcomposite.h" +#include "skycomponents/earthshadowcomponent.h" #include "skyobjects/constellationsart.h" #include "skyobjects/deepskyobject.h" #include "skyobjects/ksasteroid.h" @@ -39,6 +40,7 @@ #include "skyobjects/kssun.h" #include "skyobjects/satellite.h" #include "skyobjects/supernova.h" +#include "skyobjects/ksearthshadow.h" #include "hips/hipsrenderer.h" namespace @@ -416,7 +418,7 @@ if (fakeStarSize > 15.0) fakeStarSize = 15.0; - float size = planet->angSize() * dms::PI * Options::zoomFactor() / 10800.0; + double size = planet->angSize() * dms::PI * Options::zoomFactor() / 10800.0; if (size < fakeStarSize && planet->name() != "Sun" && planet->name() != "Moon") { // Draw them as bright stars of appropriate color instead of images @@ -458,30 +460,54 @@ save(); translate(pos); rotate(m_proj->findPA(planet, pos.x(), pos.y())); - drawImage(QRect(-0.5 * size, -0.5 * size, size, size), planet->image()); + drawImage(QRectF(-0.5 * size, -0.5 * size, size, size), planet->image()); restore(); } else //Otherwise, draw a simple circle. { - drawEllipse(pos, size, size); + drawEllipse(pos, size * .5, size * .5); } } return true; } +bool SkyQPainter::drawEarthShadow(KSEarthShadow *shadow) +{ + if (!m_proj->checkVisibility(shadow)) + return false; + + bool visible = false; + QPointF pos = m_proj->toScreen(shadow, true, &visible); + + if(!visible) + return false; + + double umbra_size = shadow->getUmbraAngSize() * dms::PI * Options::zoomFactor() / 10800.0; + double penumbra_size = shadow->getPenumbraAngSize() * dms::PI * Options::zoomFactor() / 10800.0; + + save(); + setBrush(QBrush(QColor(255, 96, 38, 128))); + drawEllipse(pos, umbra_size, umbra_size); + setBrush(QBrush(QColor(255, 96, 38, 90))); + drawEllipse(pos, penumbra_size, penumbra_size); + restore(); + + return true; +} + bool SkyQPainter::drawComet(KSComet *com) { if (!m_proj->checkVisibility(com)) return false; - float size = com->angSize() * dms::PI * Options::zoomFactor() / 10800.0; + double size = com->angSize() * dms::PI * Options::zoomFactor() / 10800.0 / 2; // Radius if (size < 1) size = 1; bool visible = false; QPointF pos = m_proj->toScreen(com, true, &visible); - // Draw the coma. + // Draw the coma. FIXME: Another Check?? if (visible && m_proj->onScreen(pos)) { // Draw the comet. @@ -612,7 +638,7 @@ translate(constellationmidpoint); rotate(positionangle); setOpacity(0.7); - drawImage(QRect(-0.5 * w, -0.5 * h, w, h), obj->image()); + drawImage(QRectF(-0.5 * w, -0.5 * h, w, h), obj->image()); setOpacity(1); setRenderHint(QPainter::SmoothPixmapTransform, false); @@ -675,7 +701,7 @@ save(); translate(pos); rotate(positionAngle); - drawImage(QRect(-0.5 * w, -0.5 * h, w, h), obj->image()); + drawImage(QRectF(-0.5 * w, -0.5 * h, w, h), obj->image()); restore(); return true; Index: kstars/tools/approachsolver.h =================================================================== --- /dev/null +++ kstars/tools/approachsolver.h @@ -0,0 +1,172 @@ +/*************************************************************************** + ApproachSolver.h - K Desktop Planetarium + ------------------- + begin : Tue 18/09/2018 + copyright : (C) 2008 by Akarsh Simha, 2018 Valentin Boettcher + email : kstar@bas.org.in, valentin@boettcher.cf + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#pragma once +#include "dms.h" +#include "skyobjects/ksplanet.h" +#include "skycomponents/typedef.h" + +#include +#include +#include +#include + +/** + * @class ApproachSolver + * @short Implements algorithms to find close approaches of two objects on the sky. + * A class that implements a method to compute close approaches between any two solar system + * objects excluding planetary moons. Given two such objects, this class has implementations of + * algorithms required to find the time of closest approach in a given range of time. It is meant + * to be subclassed and provides the boilerplate (+ common interface) for classes like KSKonjunct + * and EclipseHandler. + * + * @author Akarsh Simha + * @version 2.0 + */ +class ApproachSolver : public QObject +{ + Q_OBJECT +public: + explicit ApproachSolver(QObject *parent = nullptr); + + /** + * @short Sets the geographic location to compute conjunctions at + * + * @param geo Pointer to the GeoLocation object + */ + void setGeoLocation(GeoLocation *geo); + + /** + * @short Compute the closest approach of two planets in the given range + * + * @param Object1 A copy of the class corresponding to one of the two bodies + * @param Object2 A copy of the class corresponding to the other of the two bodies + * @param startJD Julian Day corresponding to start of the calculation period + * @param stopJD Julian Day corresponding to end of the calculation period + * @param maxSeparation Maximum separation between Object1 and Object2 - a measure + * how close the conjunction should be to be output. + * @param opposition A parameter to see if we are computing conjunction or opposition + * @return Hash containing julian days of close conjunctions against separation + */ + QMap findClosestApproach(long double startJD, + long double stopJD, + const std::function &callback = {}); // FIXME: QMap is awkward! + + /** + * @brief getGeoLocation + * @return the currently set GeoLocation + */ + GeoLocation * getGeoLocation() { return m_geoPlace; } + + + /** + * @brief setMaxSeparation + * @param sep - maximum allowed anglar separation + */ + void setMaxSeparation(double sep) { m_maxSeparation = sep; } + void setMaxSeparation(dms sep) { m_maxSeparation = sep.radians(); } + +signals: + /** + * @brief solverMadeProgress + * @param progress - progress in percent + */ + void solverMadeProgress(int progress); + +protected: + // TODO: This one may be moved to KSConjunct + + /** + * @brief getMaxSeparation + * @return the maximum separation allowed, based on the (guaranteed to be up-to-date) + * parameters of the objects if overwritten. Here it's just a constant. + */ + virtual double getMaxSeparation() { return m_maxSeparation; } + + /** + * @brief findSkyPointDistance + * @param obj1 + * @param obj2 + * @return the angular distance between two SkyPoints in arcminutes + */ + dms findSkyPointDistance(SkyPoint * obj1, SkyPoint * obj2); + + /** + * @short Finds the angular distance between two solar system objects. + * + * @param Object1 A pointer to the first solar system object + * @param Object2 A pointer to the second solar system object + * + * @return The angular distance between the two bodies. + */ + virtual dms findDistance() = 0; + + /** + * @brief updatePositions + * @short Update the positions of the objects involved. + * @param jd Julian Day corresponding to the time of computation + */ + virtual void updatePositions(long double jd) = 0; + + /** + * @brief findStep + * @return the step size used by findClosestApproach (in Julian Days) + * + * @short Make this as big as possible. The bigger it is, the more likely is a skip over... + */ + virtual double findInitialStep(long double startJD, long double stopJD) = 0; + + /** + * @short Compute the precise value of the extremum once the extremum has been detected. + * + * @param out A pointer to a QPair that stores the Julian Day and Separation corresponding to the extremum + * @param Object1 A pointer to the first solar system body + * @param Object2 A pointer to the second solar system body + * @param jd Julian day corresponding to the endpoint of the interval where extremum was detected. + * @param step The step in jd taken during computation earlier. (Defines the interval size) + * @param prevSign The previous sign of increment in moving from jd - step to jd + * + * @return true if the extremum is a minimum + */ + bool findPrecise(QPair *out, long double jd, + double step, int prevSign); + + KSPlanet m_Earth; + +private: + /** + * @brief updateAndFindDistance + * @param jd Julian Date for which to calculate + * @return output of ApproachSolver::findDistance + */ + dms updateAndFindDistance(long double jd) { + updatePositions(jd); + return findDistance(); + } + + /** + * @short Return the sign of an angle + * + * @param a The angle whose sign has to be returned + * + * @return (-1, 0, 1) if a.radians() is (-ve, zero, +ve) + */ + int sgn(dms a); + + GeoLocation * m_geoPlace { nullptr }; + double m_maxSeparation; +}; Index: kstars/tools/approachsolver.cpp =================================================================== --- /dev/null +++ kstars/tools/approachsolver.cpp @@ -0,0 +1,179 @@ +/*************************************************************************** + ApproachSolver.cpp - K Desktop Planetarium + ------------------- + begin : Tue 18/09/2018 + copyright : (C) 2008 by Akarsh Simha, 2018 Valentin Boettcher + email : kstar@bas.org.in, valentin@boettcher.cf + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "approachsolver.h" +#include + +ApproachSolver::ApproachSolver(QObject *parent) : QObject(parent) +{ + m_geoPlace = KStarsData::Instance()->geo(); + m_Earth = KSPlanet(I18N_NOOP("Earth"), QString(), QColor("white"), 12756.28 /*diameter in km*/); +} + +void ApproachSolver::setGeoLocation(GeoLocation *geo) +{ + if (geo != nullptr) + m_geoPlace = geo; + else + m_geoPlace = KStarsData::Instance()->geo(); +} + +// FIXME: We need a better algo for finding approaches! +QMap ApproachSolver::findClosestApproach(long double startJD, + long double stopJD, std::function const & callback) +{ + QMap Separations; + QPair extremum; + dms Dist; + dms prevDist; + + double step, step0; + int Sign, prevSign; + + // qCDebug(KSTARS) << "Entered KSConjunct::findClosestApproach() with startJD = " << (double)startJD; + // qCDebug(KSTARS) << "Initial Positional Information: \n"; + // qCDebug(KSTARS) << m_object1->name() << ": RA = " << m_object1->ra() -> toHMSString() << "; Dec = " << m_object1->dec() -> toDMSString() << "\n"; + // qCDebug(KSTARS) << m_object2->name() << ": RA = " << m_object2->ra() -> toHMSString() << "; Dec = " << m_object2->dec() -> toDMSString() << "\n"; + prevSign = 0; + + step0 = findInitialStep(startJD, stopJD); + step = step0; + // qCDebug(KSTARS) << "Initial Separation between " << m_object1->name() << " and " << m_object2->name() << " = " << (prevDist.toDMSString()); + + long double jd = startJD; + prevDist = updateAndFindDistance(jd); + jd += step; + while (jd <= stopJD) + { + int progress = int(100.0 * (jd - startJD) / (stopJD - startJD)); + emit solverMadeProgress(progress); + + Dist = updateAndFindDistance(jd); + Sign = sgn(Dist - prevDist); + // qCDebug(KSTARS) << "Dist = " << Dist.toDMSString() << "; prevDist = " << prevDist.toDMSString() << "; Difference = " << (Dist.Degrees() - prevDist.Degrees()) << "; Step = " << step; + + //How close are we to a conjunction, and how fast are we approaching one? + double factor = fabs((Dist.Degrees() - prevDist.Degrees()) / Dist.Degrees()); + if (factor > 10.0) //let's go faster! + { + step = step0 * factor / 10.0; + } + else //slow down, we're getting close! + { + step = step0; + } + + if (Sign != prevSign && prevSign == -1) //all right, we may have just passed a conjunction + { + if (step > step0) //mini-loop to back up and make sure we're close enough + { + // qCDebug(KSTARS) << "Entering slow loop: " << endl; + jd -= step; + step = step0; + Sign = prevSign; + while (jd <= stopJD) + { + Dist = updateAndFindDistance(jd); + Sign = sgn(Dist - prevDist); + // qCDebug(KSTARS) << "Dist=" << Dist.toDMSString() << "; prevDist=" << prevDist.toDMSString() << "; Diff=" << (Dist.Degrees() - prevDist.Degrees()) << "djd=" << (int)(jd - startJD); + if (Sign != prevSign) + break; + + prevDist = Dist; + prevSign = Sign; + jd += step; + } + } + + // qCDebug(KSTARS) << "Sign = " << Sign << " and " << "prevSign = " << prevSign << ": Entering findPrecise()\n"; + if (findPrecise(&extremum, jd, step, Sign)){ + if (extremum.second.radians() < getMaxSeparation()) { + Separations.insert(extremum.first, extremum.second); + + if(callback) + callback(extremum.first, extremum.second); + } + } + } + + prevDist = Dist; + prevSign = Sign; + jd += step; + } + + return Separations; +} + +bool ApproachSolver::findPrecise(QPair *out, long double jd, + double step, int prevSign) +{ + dms prevDist; + int Sign; + dms Dist; + + if (out == nullptr) + { + qCDebug(KSTARS) << "ERROR: Argument out to KSConjunct::findPrecise(...) was nullptr!"; + return false; + } + + prevDist = updateAndFindDistance(jd); + + step = -step / 2.0; + prevSign = -prevSign; + + while (true) + { + jd += step; + Dist = updateAndFindDistance(jd); + // qCDebug(KSTARS) << "Dist=" << Dist.toDMSString() << "; prevDist=" << prevDist.toDMSString() << "; Diff=" << (Dist.Degrees() - prevDist.Degrees()) << "step=" << step; + + if (fabs(step) < 1.0 / (24.0 * 60.0)) + { + out->first = jd - step / 2.0; + out->second = updateAndFindDistance(jd - step / 2.0); + if (out->second.radians() < updateAndFindDistance(jd - 5.0).radians()) + return true; + else + return false; + } + Sign = sgn(Dist - prevDist); + if (Sign != prevSign) + { + step = -step / 2.0; + Sign = -Sign; + } + prevSign = Sign; + prevDist = Dist; + } +} + +dms ApproachSolver::findSkyPointDistance(SkyPoint * obj1, SkyPoint * obj2) +{ + dms dist; + dist.setRadians(obj1->angularDistanceTo(obj2).radians()); + return dist; +} + +int ApproachSolver::sgn(dms a) +{ + // Auxiliary function used by the KSConjunct::findClosestApproach(...) + // method and the KSConjunct::findPrecise(...) method + + return ((a.radians() > 0) ? 1 : ((a.radians() < 0) ? -1 : 0)); +} + Index: kstars/tools/astrocalc.cpp =================================================================== --- kstars/tools/astrocalc.cpp +++ kstars/tools/astrocalc.cpp @@ -30,6 +30,7 @@ #include "modcalcvizequinox.h" #include "modcalcvlsr.h" #include "conjunctions.h" +#include "eclipsetool.h" #include #include @@ -173,6 +174,7 @@ //solarItem->setIcon(0,solarIcon); addTreeItem(solarItem, i18n("Planets Coordinates")); addTreeItem(solarItem, i18n("Conjunctions")); + addTreeItem(solarItem, i18n("Eclipses")); acStack->setCurrentWidget(splashScreen); connect(navigationPanel, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, Index: kstars/tools/conjunctions.h =================================================================== --- kstars/tools/conjunctions.h +++ kstars/tools/conjunctions.h @@ -30,7 +30,7 @@ #include #include #include - +#include "skycomponents/typedef.h" #include class QSortFilterProxyModel; @@ -40,6 +40,8 @@ class KSPlanetBase; class SkyObject; +//FIXME: URGENT! There's a bug when setting max sep to 0! + /** * @short Predicts conjunctions using KSConjunct in the background */ @@ -57,6 +59,7 @@ void slotCompute(); void showProgress(int); void slotFindObject(); + void setMode(int); void slotGoto(); void slotFilterType(int); void slotClear(); @@ -67,10 +70,26 @@ void showConjunctions(const QMap &conjunctionlist, const QString &object1, const QString &object2); - SkyObject* Object1 = nullptr; - std::unique_ptr Object2; // Second object is always a planet. + /** + * @brief setUpConjunctionOpposition + * @short set up ui for conj./opp. + */ + void setUpConjunctionOpposition(); + + /** + * @brief mode + * @short Represents wether the tool looks for conj/opp. + */ + enum MODE { + CONJUNCTION, + OPPOSITION + } mode; + + SkyObject_s Object1; + KSPlanetBase_s Object2; // Second object is always a planet. /// To store the names of Planets vs. values expected by KSPlanetBase::createPlanet() QHash pNames; + /// To store Julian Days corresponding to the row index in the output list widget QMap outputJDList; GeoLocation *geoPlace { nullptr }; Index: kstars/tools/conjunctions.cpp =================================================================== --- kstars/tools/conjunctions.cpp +++ kstars/tools/conjunctions.cpp @@ -34,6 +34,7 @@ #include "skycomponents/skymapcomposite.h" #include "skyobjects/kscomet.h" #include "skyobjects/kspluto.h" +#include "ksplanetbase.h" #include #include @@ -57,19 +58,6 @@ geoPlace = kd->geo(); LocationButton->setText(geoPlace->fullName()); - // Init filter type combobox - FilterTypeComboBox->addItem(i18n("Single Object...")); - FilterTypeComboBox->addItem(i18n("Any")); - FilterTypeComboBox->addItem(i18n("Stars")); - FilterTypeComboBox->addItem(i18n("Solar System")); - FilterTypeComboBox->addItem(i18n("Planets")); - FilterTypeComboBox->addItem(i18n("Comets")); - FilterTypeComboBox->addItem(i18n("Asteroids")); - FilterTypeComboBox->addItem(i18n("Open Clusters")); - FilterTypeComboBox->addItem(i18n("Globular Clusters")); - FilterTypeComboBox->addItem(i18n("Gaseous Nebulae")); - FilterTypeComboBox->addItem(i18n("Planetary Nebulae")); - FilterTypeComboBox->addItem(i18n("Galaxies")); pNames[KSPlanetBase::MERCURY] = i18n("Mercury"); pNames[KSPlanetBase::VENUS] = i18n("Venus"); @@ -82,36 +70,21 @@ pNames[KSPlanetBase::SUN] = i18n("Sun"); pNames[KSPlanetBase::MOON] = i18n("Moon"); - for (int i = 0; i < KSPlanetBase::UNKNOWN_PLANET; ++i) - { - // Obj1ComboBox->insertItem( i, pNames[i] ); - Obj2ComboBox->insertItem(i, pNames[i]); - } - // Initialize the Maximum Separation box to 1 degree maxSeparationBox->setDegType(true); maxSeparationBox->setDMS("01 00 00.0"); - //Set up the Table Views - m_Model = new QStandardItemModel(0, 5, this); - m_Model->setHorizontalHeaderLabels(QStringList() << i18n("Conjunction/Opposition") << i18n("Date & Time (UT)") - << i18n("Object 1") << i18n("Object 2") << i18n("Separation")); - m_SortModel = new QSortFilterProxyModel(this); - m_SortModel->setSourceModel(m_Model); - OutputList->setModel(m_SortModel); - OutputList->setSortingEnabled(true); - OutputList->horizontalHeader()->setStretchLastSection(true); - OutputList->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); - OutputList->horizontalHeader()->resizeSection(2, 100); - OutputList->horizontalHeader()->resizeSection(3, 100); - OutputList->horizontalHeader()->resizeSection(4, 120); //is it bad way to fix default size of columns ? - //FilterEdit->showClearButton = true; ClearFilterButton->setIcon(QIcon::fromTheme("edit-clear")); // signals and slots connections connect(LocationButton, SIGNAL(clicked()), this, SLOT(slotLocation())); connect(Obj1FindButton, SIGNAL(clicked()), this, SLOT(slotFindObject())); + + // Mode Change + connect(ModeSelector, QOverload::of(&QComboBox::currentIndexChanged), this, + &ConjunctionsTool::setMode); + //connect(ComputeButton, SIGNAL(clicked()), this, SLOT(slotCompute())); connect(ComputeButton, &QPushButton::clicked, [this]() { @@ -124,6 +97,47 @@ connect(ClearFilterButton, SIGNAL(clicked()), FilterEdit, SLOT(clear())); connect(FilterEdit, SIGNAL(textChanged(QString)), this, SLOT(slotFilterReg(QString))); + m_Model = new QStandardItemModel(0, 5, this); + + setMode(ModeSelector->currentIndex()); + + // Init filter type combobox + FilterTypeComboBox->clear(); + FilterTypeComboBox->addItem(i18n("Single Object...")); + FilterTypeComboBox->addItem(i18n("Any")); + FilterTypeComboBox->addItem(i18n("Stars")); + FilterTypeComboBox->addItem(i18n("Solar System")); + FilterTypeComboBox->addItem(i18n("Planets")); + FilterTypeComboBox->addItem(i18n("Comets")); + FilterTypeComboBox->addItem(i18n("Asteroids")); + FilterTypeComboBox->addItem(i18n("Open Clusters")); + FilterTypeComboBox->addItem(i18n("Globular Clusters")); + FilterTypeComboBox->addItem(i18n("Gaseous Nebulae")); + FilterTypeComboBox->addItem(i18n("Planetary Nebulae")); + FilterTypeComboBox->addItem(i18n("Galaxies")); + + Obj2ComboBox->clear(); + for (int i = 0; i < KSPlanetBase::UNKNOWN_PLANET; ++i) + { + // Obj1ComboBox->insertItem( i, pNames[i] ); + Obj2ComboBox->insertItem(i, pNames[i]); + } + + maxSeparationBox->setEnabled(true); + + //Set up the Table Views + m_Model->setHorizontalHeaderLabels(QStringList() << i18n("Conjunction/Opposition") << i18n("Date & Time (UT)") + << i18n("Object 1") << i18n("Object 2") << i18n("Separation")); + m_SortModel = new QSortFilterProxyModel(this); + m_SortModel->setSourceModel(m_Model); + OutputList->setModel(m_SortModel); + OutputList->setSortingEnabled(true); + OutputList->horizontalHeader()->setStretchLastSection(true); + OutputList->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); + OutputList->horizontalHeader()->resizeSection(2, 100); + OutputList->horizontalHeader()->resizeSection(3, 100); + OutputList->horizontalHeader()->resizeSection(4, 120); //is it bad way to fix default size of columns ? + show(); } @@ -152,13 +166,24 @@ { if (!fd->targetObject()) return; - Object1 = fd->targetObject(); + Object1 = SkyObject_s(fd->targetObject()->clone()); if (Object1 != nullptr) Obj1FindButton->setText(Object1->name()); } delete fd; } +void ConjunctionsTool::setMode(int new_mode) +{ + // unlikely to happen + if(new_mode == -1 || new_mode > 2){ + ModeSelector->setCurrentIndex(0); + return; + } + + mode = static_cast(new_mode); +} + void ConjunctionsTool::slotLocation() { QPointer ld(new LocationDialog(this)); @@ -226,7 +251,7 @@ long double startJD = dtStart.djd(); // Start julian day long double stopJD = dtStop.djd(); // Stop julian day bool opposition = false; // true=opposition, false=conjunction - if (Opposition->currentIndex()) + if (mode == OPPOSITION) opposition = true; QStringList objects; // List of sky object used as Object1 KStarsData *data = KStarsData::Instance(); @@ -330,6 +355,10 @@ objects.removeAll("Iapetus"); } + ksc.setMaxSeparation(maxSeparation); + ksc.setObject2(Object2); + ksc.setOpposition(opposition); + if (FilterTypeComboBox->currentIndex() != 0) { // Show a progress dialog while processing @@ -350,8 +379,9 @@ progressDlg.setLabelText(i18n("Compute conjunction between %1 and %2", Object2->name(), object)); // Compute conjuction - Object1 = data->skyComposite()->findByName(object); - showConjunctions(ksc.findClosestApproach(*Object1, *Object2, startJD, stopJD, maxSeparation, opposition), + Object1 = std::shared_ptr(data->skyComposite()->findByName(object)->clone()); + ksc.setObject1(Object1); + showConjunctions(ksc.findClosestApproach(startJD, stopJD), object, Object2->name()); } @@ -363,7 +393,9 @@ QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); ComputeStack->setCurrentIndex(1); - showConjunctions(ksc.findClosestApproach(*Object1, *Object2, startJD, stopJD, maxSeparation, opposition), + + ksc.setObject1(Object1); + showConjunctions(ksc.findClosestApproach(startJD, stopJD), Object1->name(), Object2->name()); ComputeStack->setCurrentIndex(0); @@ -390,7 +422,7 @@ dt.setDJD(it.key()); QStandardItem *typeItem; - if (!Opposition->currentIndex()) + if (mode == CONJUNCTION) typeItem = new QStandardItem(i18n("Conjunction")); else typeItem = new QStandardItem(i18n("Opposition")); @@ -408,3 +440,8 @@ ++m_index; } } + +void ConjunctionsTool::setUpConjunctionOpposition() +{ + +} Index: kstars/tools/conjunctions.ui =================================================================== --- kstars/tools/conjunctions.ui +++ kstars/tools/conjunctions.ui @@ -153,7 +153,7 @@ - + 0 Index: kstars/tools/eclipsehandler.h =================================================================== --- /dev/null +++ kstars/tools/eclipsehandler.h @@ -0,0 +1,183 @@ +/*************************************************************************** + eclipsehandler.h - K Desktop Planetarium + ------------------- + begin : Tue 18/09/2018 + copyright : (C) 2018 Valentin Boettcher + email : valentin@boettcher.cf + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#pragma once +#include "geolocation.h" +#include "approachsolver.h" +#include "skycomponents/typedef.h" + +class KSPlanetBase; + +/** + * @brief The EclipseEvent class + * @short Abstract container/interface for a eclipse event. + * @note We do not use the QObject hirachy here as it would be ineficcient + */ +class EclipseEvent : public QObject { + Q_OBJECT + +public: + /** + * @brief The ECLIPSE_TYPE_T enum + * @short Basic eclipse type. May be supplemented + * by subclasses. + */ + enum ECLIPSE_TYPE { + PARTIAL, + FULL + }; + + EclipseEvent(long double jd, GeoLocation geoPlace, ECLIPSE_TYPE type); + + virtual ~EclipseEvent(); // empty for now + + /** + * @brief getJD + * @return the julian date of the event + */ + long double getJD() { return m_jd; } + + /** + * @brief getExtraInfo + * @return information to display in an extra collumn + * of the overview table. + */ + virtual QString getExtraInfo() { return ""; } + + /** + * @brief getType + * @return the type of the eclipse + */ + ECLIPSE_TYPE getType() { return m_type; } + + /** + * @brief getEclipsingObjectName + * @return the name of the eclipsing object + * @note maybe store those objects as clones... + */ + virtual QString getEclipsingObjectName() = 0; + + /** + * @brief getEclipsingObjectName + * @return the name of the eclipsed object + * @note maybe store those objects as clones... + */ + virtual QString getEclipsedObjectName() = 0; + + /** + * @brief getGeolocation + * @return geolocation for which the event is valid + */ + GeoLocation getGeolocation() { return m_geoPlace; } + + /** + * @brief getEclipsingObjectFromSkyComposite + * @return a pointer to the skymap instance of the ecl. obj. + */ + virtual SkyObject * getEclipsingObjectFromSkyComposite() = 0; + + /** + * @brief hasDeails + * @return wether a details widget can be shown + */ + virtual bool hasDeails() { return false; } + +public slots: + /** + * @brief showDetails + * @short (if implemented) shows a widget with details about the eclipse + */ + virtual void slotShowDetails() { return; } + +private: + ECLIPSE_TYPE m_type; + GeoLocation m_geoPlace; + + /** + * @brief jd - date of the event + */ + long double m_jd; +}; + +/** + * @brief The EclipseHandler class + * + * This is a base class for providing a common interface + * for eclipse events which can be quite different in nature. It is + * meant to be subclassed. (Check LunarEclipseHandler as an example) + * + * @todo remove uglieness from KSConjunct (export m_object... functionality to separate class!, + * that findinitialstepsize isn't nice either) + * + * @note I've integrated the `findDetails` stuff in the eclipse handler because it already has + * the `beef` it takes (instances and methods). OOP is not always the way. + */ +class EclipseHandler : public ApproachSolver +{ + Q_OBJECT +public: + typedef QVector EclipseVector; + + EclipseHandler(QObject * parent = nullptr); + virtual ~EclipseHandler() override; + + /** + * @brief compute + * @short Implements the details for finding *all* the eclipses + * in a given time-frame. Should call findEclipse intelligently. + * e.g. only if the moon is full for lunar eclipses et-cetera + * + * @returns A vector of shared pointers to eclipse events. + */ + virtual EclipseVector computeEclipses(long double startJD, long double endJD) = 0; + + /** + * @brief getEvents + * @short May be used if the return value of computeEclipses is being ignored. + * @note The underlying vector changes after every call to computeEclipses. + * + * @return A vector of shared pointers to eclipse events. + */ + QVector getEvents() { return m_events; } + +signals: + /** + * @brief signalEventFound + * @short A signal to be dispatched as soon as a new Event is found. + * @note Has to emited by a subclass! + * @param event + */ + void signalEventFound(EclipseEvent_s event); + + /** + * @brief signalProgress + * @short gives the progress of the computation in percent + */ + void signalProgress(int); + + /** + * @brief signalComputationFinished + * @short signals the end of the computation + */ + void signalComputationFinished(); + +protected: + virtual double findInitialStep(long double startJD, long double stopJD) override { return double(stopJD - startJD) / 4.0; } + +private: + QVector m_events; +}; Index: kstars/tools/eclipsehandler.cpp =================================================================== --- /dev/null +++ kstars/tools/eclipsehandler.cpp @@ -0,0 +1,38 @@ +/*************************************************************************** + eclipsehandler.cpp - K Desktop Planetarium + ------------------- + begin : Tue 18/09/2018 + copyright : (C) 2018 Valentin Boettcher + email : valentin@boettcher.cf + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "eclipsehandler.h" + +EclipseEvent::EclipseEvent(long double jd, GeoLocation geoPlace, ECLIPSE_TYPE type) : QObject(), + m_type { type }, m_geoPlace { geoPlace }, m_jd { jd } +{ + qRegisterMetaType("EclipseEvent_s"); +} + +EclipseEvent::~EclipseEvent() +{ + +} + +EclipseHandler::EclipseHandler(QObject * parent) : ApproachSolver (parent) +{ +} + +EclipseHandler::~EclipseHandler() +{ + +} Index: kstars/tools/eclipsestool.h =================================================================== --- /dev/null +++ kstars/tools/eclipsestool.h @@ -0,0 +1,22 @@ +#ifndef ECLIPSESTOOL_H +#define ECLIPSESTOOL_H + +#include + +namespace Ui { +class EclipsesTool; +} + +class EclipsesTool : public QFrame +{ + Q_OBJECT + +public: + explicit EclipsesTool(QWidget *parent = nullptr); + ~EclipsesTool(); + +private: + Ui::EclipsesTool *ui; +}; + +#endif // ECLIPSESTOOL_H Index: kstars/tools/eclipsestool.ui =================================================================== --- /dev/null +++ kstars/tools/eclipsestool.ui @@ -0,0 +1,19 @@ + + + EclipsesTool + + + + 0 + 0 + 400 + 300 + + + + Frame + + + + + Index: kstars/tools/eclipsetool.h =================================================================== --- /dev/null +++ kstars/tools/eclipsetool.h @@ -0,0 +1,139 @@ +/*************************************************************************** + eclipsetool.h - K Desktop Planetarium + ------------------- + begin : Tue 18/09/2018 + copyright : (C) 2018 Valentin Boettcher + email : valentin@boettcher.cf + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#pragma once +#include +#include +#include +#include +#include "eclipsetool/lunareclipsehandler.h" + +namespace Ui { +class EclipseTool; +} + +class KSPlanetBase; +class QComboBox; +class GeoLocation; + +/** + * @brief The EclipseModel class + * @short A simple model to contain EclipseEvents. + */ +class EclipseModel : public QAbstractTableModel +{ + Q_OBJECT +public: + // This is reimplemented boilerplate from QAbstractModel + EclipseModel(QObject * parent = nullptr); + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const { return COLLUMNS; } + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + +public slots: + /** + * @brief reset + * @short resets the data and the model + */ + void reset(); + + /** + * @brief slotAddEclipse + * @param eclipse + * @short add an eclipse to the model + */ + void slotAddEclipse(EclipseEvent_s eclipse); + + /** + * @brief exportAsCsv + * @short Export data as CSV (plus dialog) + */ + void exportAsCsv(); + + /** + * @brief getEvent + * @param index + * @short retrieve an event + */ + EclipseEvent_s getEvent(int index) { return m_eclipses.at(index); } + +private slots: + // reimplemented to clear the data + void resetInternalData() { m_eclipses.clear(); } + +private: + EclipseHandler::EclipseVector m_eclipses; + const int COLLUMNS { 5 }; +}; + +/** + * @brief The EclipseTool class + * @short The UI for the Eclipsetool. + * Currently only lunar eclipses are implemented. + * Others will follow. + */ +class EclipseTool : public QFrame +{ + Q_OBJECT + +public: + explicit EclipseTool(QWidget *parent = nullptr); + ~EclipseTool(); + +private slots: + /** + * @brief slotLocation + * @short slot for setting the geolocation + */ + void slotLocation(); + + /** + * @brief slotCompute + * @short slot to start the computation + */ + void slotCompute(); + + /** + * @brief slotFinished + * @short slot to clear any residuals + */ + void slotFinished(); + + /** + * @brief slotContextMenu + * @short show context menu for an eclipse + * @param pos + */ + void slotContextMenu(QPoint pos); + + /** + * @brief slotView + * @param event + * @short view an eclipse on the SkyMap + */ + void slotView(EclipseEvent_s event); + +private: + Ui::EclipseTool *ui; + + QVector> m_object1List; + QVector> m_object2List; + + GeoLocation * m_geoLocation { nullptr }; + EclipseModel m_model; +}; Index: kstars/tools/eclipsetool.cpp =================================================================== --- /dev/null +++ kstars/tools/eclipsetool.cpp @@ -0,0 +1,287 @@ +/*************************************************************************** + eclipsetool.cpp - K Desktop Planetarium + ------------------- + begin : Tue 18/09/2018 + copyright : (C) 2018 Valentin Boettcher + email : valentin@boettcher.cf + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "eclipsetool.h" +#include "ui_eclipsetool.h" +#include "ksplanetbase.h" +#include "kstarsdata.h" +#include "geolocation.h" +#include "dialogs/locationdialog.h" +#include "kstars.h" +#include "skymap.h" + +#include +#include +#include +#include + +EclipseTool::EclipseTool(QWidget *parent) : + QFrame(parent), + ui(new Ui::EclipseTool) +{ + ui->setupUi(this); + + // set up the eclipse-partner selection + ui->Obj1ComboBox->addItem(i18n("Earth Shadow"), KSPlanetBase::EARTH_SHADOW); + ui->Obj2ComboBox->addItem(i18n("Moon"), KSPlanetBase::MOON); + + KStarsData *kd = KStarsData::Instance(); + KStarsDateTime dtStart(KStarsDateTime::currentDateTime()); + KStarsDateTime dtStop(dtStart.djd() + 365.24l); // one year + + m_geoLocation = kd->geo(); + ui->LocationButton->setText(m_geoLocation->fullName()); + + ui->startDate->setDateTime(dtStart); + ui->stopDate->setDateTime(dtStop); + + ui->ClearButton->setIcon(QIcon::fromTheme("edit-clear")); + + // set up slots + connect(ui->LocationButton, &QPushButton::clicked, this, &EclipseTool::slotLocation); + connect(ui->ComputeButton, &QPushButton::clicked, this, [&, this]() { + // switch to progress bar + ui->computeStack->setCurrentIndex(1); + + // reset progress + ui->progressBar->setValue(0); + + QtConcurrent::run(this, &EclipseTool::slotCompute); + }); + + connect(ui->ClearButton, &QPushButton::clicked, &m_model, &EclipseModel::reset); + connect(ui->ExportButton, &QPushButton::clicked, &m_model, &EclipseModel::exportAsCsv); + + // eclipse table + ui->tableView->setModel(&m_model); + ui->tableView->horizontalHeader()->setStretchLastSection(true); + ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu); + + // double click to view + connect(ui->tableView, &QTableView::doubleClicked, this, [this](const QModelIndex &index){ + slotView(m_model.getEvent(index.row())); + }); + + connect(ui->tableView, &QTableView::customContextMenuRequested, this, &EclipseTool::slotContextMenu); + + ui->progressBar->setMinimum(0); + ui->progressBar->setMaximum(100); +} + +EclipseTool::~EclipseTool() +{ + delete ui; +} + +void EclipseTool::slotLocation() +{ + QPointer ld(new LocationDialog(this)); + if (ld->exec() == QDialog::Accepted && ld) + { + m_geoLocation = ld->selectedCity(); + ui->LocationButton->setText(m_geoLocation->fullName()); + } + delete ld; +} + +// NOTE: This will need redesign some day. +void EclipseTool::slotCompute() +{ + EclipseHandler * handler; + KStarsDateTime dtStart(ui->startDate->dateTime()); // start date + KStarsDateTime dtStop(ui->stopDate->dateTime()); // stop date + long double startJD = dtStart.djd(); // start julian day + long double stopJD = dtStop.djd(); + + if(ui->Obj1ComboBox->currentData().toInt() == KSPlanetBase::EARTH_SHADOW && ui->Obj2ComboBox->currentData().toInt() == KSPlanetBase::MOON){ + handler = new LunarEclipseHandler(); + } else + return; + + connect(handler, &EclipseHandler::signalEventFound, &m_model, &EclipseModel::slotAddEclipse); + connect(handler, &EclipseHandler::signalProgress, ui->progressBar, &QProgressBar::setValue); + connect(handler, &EclipseHandler::signalComputationFinished, this, &EclipseTool::slotFinished); + + handler->setGeoLocation(m_geoLocation); + + // let's go + EclipseHandler::EclipseVector eclipses = handler->computeEclipses(startJD, stopJD); + delete handler; +} + +void EclipseTool::slotFinished() +{ + ui->computeStack->setCurrentIndex(0); +} + +void EclipseTool::slotContextMenu(QPoint pos) +{ + int row = ui->tableView->indexAt(pos).row(); + EclipseEvent_s event = m_model.getEvent(row); + + QMenu * menu = new QMenu(this); + QAction * view = new QAction(i18n("View in SkyMap"), menu); + connect(view, &QAction::triggered, this, [=]{ + slotView(event); + }); + menu->addAction(view); + + if(event->hasDeails()){ + QAction * details = new QAction(i18n("Show Details"), menu); + connect(details, &QAction::triggered, this, [=] { + event->slotShowDetails(); // call virtual + }); + menu->addAction(details); + } + + menu->popup(ui->tableView->viewport()->mapToGlobal(pos)); +} + +void EclipseTool::slotView(EclipseEvent_s event) +{ + SkyMap * map = KStars::Instance()->map(); + KStarsData * data = KStarsData::Instance(); + KStarsDateTime dt; + + dt.setDJD(event->getJD()); + data->changeDateTime(dt); + data->setLocation(event->getGeolocation()); + + map->setClickedObject(event->getEclipsingObjectFromSkyComposite()); + map->setClickedPoint(map->clickedObject()); + map->slotCenter(); +} + +EclipseModel::EclipseModel(QObject *parent) : QAbstractTableModel (parent) +{ + +} + +int EclipseModel::rowCount(const QModelIndex &) const +{ + return m_eclipses.length(); +} + +QVariant EclipseModel::data(const QModelIndex &index, int role) const +{ + int row = index.row(); + int col = index.column(); + + if(role != Qt::DisplayRole) + return QVariant(); + + EclipseEvent * event = m_eclipses[row].get(); + + switch(col) { + case 0: /* Date */ { + KStarsDateTime dt(event->getJD()); + return dt.toString(Qt::ISODate); + } + case 1: // Eclipsing Obj + return event->getEclipsingObjectName(); + case 2: // Eclipsed Obj + return event->getEclipsedObjectName(); + case 3: /* Eclipse Type */ { + switch(event->getType()) { + case EclipseEvent::FULL: + return QString(i18n("Full")); + case EclipseEvent::PARTIAL: + return QString(i18n("Partial")); + } + break; + } + case 4: // Extra Info + return event->getExtraInfo(); + } + + return QVariant(); +} + +void EclipseModel::slotAddEclipse(EclipseEvent_s eclipse) +{ + m_eclipses.append(eclipse); + emit layoutChanged(); // there must be a better way +} + +void EclipseModel::exportAsCsv() +{ + int i, j; + QByteArray line; + + QFileDialog dialog; + dialog.setNameFilter(i18n("CSV Files (*.csv)")); + dialog.setDefaultSuffix("csv"); + dialog.setWindowTitle(i18n("Export Eclipses")); + + QString fname; + if(dialog.exec()) + fname = dialog.selectedFiles()[0]; + else { + QErrorMessage msg; + msg.showMessage(i18n("Could not export.")); + return; + } + + QFile file(fname); + + file.open(QIODevice::WriteOnly | QIODevice::Text); + + for (i = 0; i < rowCount(); ++i) + { + for (j = 0; j < columnCount(); ++j) + { + line.append(data(index(i, j), Qt::DisplayRole).toByteArray()); + if (j < columnCount() - 1) + line.append(";"); + else + line.append("\n"); + } + file.write(line); + line.clear(); + } + + file.close(); +} + +QVariant EclipseModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::DisplayRole) + { + if (orientation == Qt::Horizontal) { + switch (section) + { + case 0: + return QString("Date"); + case 1: + return QString("Eclipsing Object"); + case 2: + return QString("Eclipsed Object"); + case 3: + return QString("Eclipse Type"); + case 4: + return QString("Extra Information"); + } + } + } + return QVariant(); +} + +void EclipseModel::reset() +{ + emit beginResetModel(); + emit endResetModel(); +} Index: kstars/tools/eclipsetool.ui =================================================================== --- /dev/null +++ kstars/tools/eclipsetool.ui @@ -0,0 +1,207 @@ + + + EclipseTool + + + + 0 + 0 + 681 + 445 + + + + Frame + + + + + + QLayout::SetMinimumSize + + + + + Ending on: + + + + + + + + 0 + 0 + + + + Between objects: + + + + + + + true + + + + + + + + + + + + and + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Show eclipses for: + + + + + + + Starting on: + + + + + + + Greenwich, United Kingdom + + + + + + + true + + + + + + + + + + 16777215 + 43 + + + + 0 + + + + + + + 6 + + + QLayout::SetMinimumSize + + + + + Compute + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Export + + + + + + + Clear + + + + + + + + + + + + + 24 + + + + + + + + + + + Results + + + + + + + + + + + + + Index: kstars/tools/eclipsetool/lunareclipsehandler.h =================================================================== --- /dev/null +++ kstars/tools/eclipsetool/lunareclipsehandler.h @@ -0,0 +1,144 @@ +/*************************************************************************** + lunareclipsehandler.h - K Desktop Planetarium + ------------------- + begin : Tue 18/09/2018 + copyright : (C) 2018 Valentin Boettcher + email : valentin@boettcher.cf + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#pragma once +#include "eclipsehandler.h" +#include "ksearthshadow.h" +#include "ksmoon.h" +#include "kssun.h" // NOTE: Maybe use pointers... compile time savings + +// FIXME: (Valentin) WIP, To be completed (extra features)! + +/** + * @brief The LunarEclipseDetails struct + * @short A struct to hold detail informoation about an eclipse + */ +struct LunarEclipseDetails { + enum EVENT { + BEGIN_PENUMBRA_CONTACT, + BEGIN_UMBRA_CONTACT, + END_PENUMBRA_CONTACT, + END_UMBRA_CONTACT, + BEGIN_FULL_UMBRA, + END_FULL_UMBRA, + BEGIN_FULL_PENUMRA, + END_FULL_PENUMRA, + CLOSEST_APPROACH + }; + + bool available = false; + QMap eclipseTimes; + // More Later +}; + +/** + * @brief The LunarEclipseEvent class + * @short implementation of the EclipseEvent for the LunarEclipseHandler + */ +class LunarEclipseEvent : public EclipseEvent +{ + Q_OBJECT +public: + LunarEclipseEvent(long double jd, GeoLocation geoPlace, ECLIPSE_TYPE type, KSEarthShadow::ECLIPSE_TYPE detailed_type); + + ~LunarEclipseEvent() override {} // empty for now + + QString getExtraInfo() override; + + /** + * @brief getDetailedType + * @return Type of the eclipse as in KSEarthShadow::ECLIPSE_TYPE + */ + KSEarthShadow::ECLIPSE_TYPE getDetailedType() { return m_detailedType; } + + QString getEclipsingObjectName() override { return i18n("Earth Shadow"); } + QString getEclipsedObjectName() override { return i18n("Moon"); } + + SkyObject * getEclipsingObjectFromSkyComposite() override; + + bool hasDeails() override { return false; } // false for now! + +public slots: + void slotShowDetails() override; + +private: + KSEarthShadow::ECLIPSE_TYPE m_detailedType; + LunarEclipseDetails m_details; + // More Later_details; +}; + + +/** + * @brief The LunarEclipseHandler class + * @short Calculate lunar eclipses. + * + * Calculates lunar eclipses by looking for them close to full moon. + */ +class LunarEclipseHandler : public EclipseHandler +{ + Q_OBJECT +public: + explicit LunarEclipseHandler(QObject * parent = nullptr); + virtual ~LunarEclipseHandler() override; + + EclipseVector computeEclipses(long double startJD, long double endJD) override; + + // FIXME: (Valentin) Not yet finished. Returns empty Details! + LunarEclipseDetails findEclipseDetails(LunarEclipseEvent *event); + +protected: + double findInitialStep(long double, long double) override + { return (m_mode == CLOSEST_APPROACH) ? INITIAL_STEP : DETAIL_STEP; } + + void updatePositions(long double jd) override; + + // NOTE: This method depends on m_mode! + dms findDistance() override; + + // NOTE: This method depends on m_mode! + double getMaxSeparation() override; + +private: + /** + * @brief getFullMoons + * @param startJD start Date + * @param endJD end Date + * @return a vector of JDs for full moons (actually a little earlier as it doesnt matter much) + */ + QVector getFullMoons(long double startJD, long double endJD); + + // Objects for the Calculations + KSSun m_sun; + KSMoon m_moon; + KSEarthShadow m_shadow; + + // Controls the step size, is chosen small to minimize overshooting + const double INITIAL_STEP { 0.1 }; + const double DETAIL_STEP { 0.001 }; + + /** + * @brief The MODE enum + * @short Set the mode for the distance minimizer, i.e. which point to find. + */ + enum { + CLOSEST_APPROACH, + PENUMBRA_CONTACT, + PUNUMBRA_IMMERSION, + UMBRA_CONTACT, + UMBRA_IMMERSION + } m_mode { CLOSEST_APPROACH }; +}; Index: kstars/tools/eclipsetool/lunareclipsehandler.cpp =================================================================== --- /dev/null +++ kstars/tools/eclipsetool/lunareclipsehandler.cpp @@ -0,0 +1,246 @@ +/*************************************************************************** + lunareclipsehandler.cpp - K Desktop Planetarium + ------------------- + begin : Tue 18/09/2018 + copyright : (C) 2018 Valentin Boettcher + email : valentin@boettcher.cf + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "lunareclipsehandler.h" +#include "skymapcomposite.h" +#include "solarsystemcomposite.h" +#include "dms.h" + +LunarEclipseHandler::LunarEclipseHandler(QObject * parent) : EclipseHandler(parent), + m_sun(), m_moon(), m_shadow(&m_moon, &m_sun, &m_Earth) +{ +} + +EclipseHandler::EclipseVector LunarEclipseHandler::computeEclipses(long double startJD, long double endJD) +{ + m_mode = CLOSEST_APPROACH; + + const long double SEARCH_INTERVAL = 5.l; // Days + + QVector eclipses; + QVector fullMoons = getFullMoons(startJD, endJD); + + int total = fullMoons.length(); + if (total == 0) + return eclipses; + + float step = 1/total; + float progress = 0; + + connect(this, &ApproachSolver::solverMadeProgress, this, [&, this] (int dProgress) { + float tmpProgress = roundf(progress + step * dProgress); + if (tmpProgress > progress) { + progress = tmpProgress; + emit signalProgress(static_cast(progress)); + } + }); + + for(auto date : fullMoons) { + findClosestApproach(date, date + SEARCH_INTERVAL, [&eclipses, this] (long double JD, dms) { + EclipseEvent::ECLIPSE_TYPE type; + updatePositions(JD); + + KSEarthShadow::ECLIPSE_TYPE extended_type = m_shadow.getEclipseType(); + switch (extended_type) { + case KSEarthShadow::FULL_PENUMBRA: + case KSEarthShadow::FULL_UMBRA: + type = EclipseEvent::FULL; + break; + case KSEarthShadow::NONE: + return; + default: + type = EclipseEvent::PARTIAL; + break; + } + + EclipseEvent_s event = std::make_shared(JD, *getGeoLocation(), type, extended_type); + emit signalEventFound(event); + eclipses.append(event); + }); + + progress++; + emit signalProgress(static_cast(roundf(100*(progress/total)))); + } + + emit signalProgress(100); + emit signalComputationFinished(); + return eclipses; +} + +// FIXME: (Valentin) This doesn't work for now. We need another method. +LunarEclipseDetails LunarEclipseHandler::findEclipseDetails(LunarEclipseEvent *event) +{ +// const long double INTERVAL = 1.l; + +// const long double JD = event->getJD(); +// const long double start = JD - INTERVAL; +// const long double stop = JD + INTERVAL; + + LunarEclipseDetails details; +// details.available = true; +// details.eclipseTimes.insert(JD, LunarEclipseDetails::CLOSEST_APPROACH); + +// auto type = event->getDetailedType(); +// auto findBoth = [&](LunarEclipseDetails::EVENT ev1 /* first (temporal) */, LunarEclipseDetails::EVENT ev2) { +// QMap tmpApproaches; + +// QPair out; +// findPrecise(&out, JD, 0.001, -1); +// details.eclipseTimes.insert(out.first, ev1); + +// findPrecise(&out, JD, 0.001, 1); +// details.eclipseTimes.insert(out.first, ev2); +// }; + +// // waterfall method... + +// if(type == KSEarthShadow::NONE) { +// details.available = false; +// return details; +// } + +// if(type == KSEarthShadow::FULL_UMBRA) { +// m_mode = UMBRA_IMMERSION; +// findBoth(LunarEclipseDetails::BEGIN_FULL_PENUMRA, LunarEclipseDetails::END_FULL_PENUMRA); + +// m_mode = UMBRA_CONTACT; +// findBoth(LunarEclipseDetails::BEGIN_UMBRA_CONTACT, LunarEclipseDetails::END_UMBRA_CONTACT); +// } + +//// if(type == KSEarthShadow::FULL_PENUMBRA || type == KSEarthShadow::FULL_UMBRA) { + +//// m_mode = UMR +//// }; + return details; + +} + +LunarEclipseHandler::~LunarEclipseHandler() +{ + +} + +void LunarEclipseHandler::updatePositions(long double jd) +{ + KStarsDateTime t(jd); + KSNumbers num(jd); + CachingDms LST(getGeoLocation()->GSTtoLST(t.gst())); + const CachingDms * LAT = getGeoLocation()->lat(); + + m_Earth.findPosition(&num); + m_sun.findPosition(&num, LAT, &LST, &m_Earth); + m_moon.findPosition(&num, LAT, &LST, &m_Earth); + m_shadow.findPosition(&num, LAT, &LST, &m_Earth); +} + +dms LunarEclipseHandler::findDistance() +{ + dms moon_rad = dms(m_moon.angSize() / 120); + dms pen_rad = dms(m_shadow.getPenumbraAngSize() / 60); + dms um_rad = dms(m_shadow.getUmbraAngSize() / 60); + + dms dist = findSkyPointDistance(&m_shadow, &m_moon); + switch (m_mode) { + case CLOSEST_APPROACH: + return dist; + case PENUMBRA_CONTACT: + return dist - (moon_rad + pen_rad); + case PUNUMBRA_IMMERSION: + return dist + moon_rad - pen_rad; + case UMBRA_CONTACT: + return dist - (moon_rad + um_rad); + case UMBRA_IMMERSION: + return dist + moon_rad - um_rad; + } + + return dms(); +} + +double LunarEclipseHandler::getMaxSeparation() +{ + const double SEP_QUALITY = 0.1; + + // we use the penumbra as meassure :) + if(m_mode == CLOSEST_APPROACH) + return (m_shadow.getPenumbraAngSize() + m_moon.angSize()) / 60; + else + return SEP_QUALITY; +} + +QVector LunarEclipseHandler::getFullMoons(long double startJD, long double endJD) +{ + const long double NEXT_STEP = 0.5l; + const long double INTERVAL = 26.5l; + long double & currentJD = startJD; + + QVector fullMoons; + while(currentJD <= endJD) { + KStarsDateTime t(currentJD); + KSNumbers num(currentJD); + CachingDms LST = getGeoLocation()->GSTtoLST(t.gst()); + + m_sun.updateCoords(&num, true, getGeoLocation()->lat(), &LST, true); + m_moon.updateCoords(&num, true, getGeoLocation()->lat(), &LST, true); + m_moon.findPhase(&m_sun); + + if(m_moon.illum() > 0.9) { + fullMoons.append(currentJD); + currentJD += INTERVAL; + continue; + } + + currentJD += NEXT_STEP; + } + + return fullMoons; +} + +LunarEclipseEvent::LunarEclipseEvent(long double jd, GeoLocation geoPlace, EclipseEvent::ECLIPSE_TYPE type, KSEarthShadow::ECLIPSE_TYPE detailed_type) + : EclipseEvent (jd, geoPlace, type), m_detailedType { detailed_type } +{ + m_details.available = false; +} + +QString LunarEclipseEvent::getExtraInfo() +{ + switch(m_detailedType) + { + case KSEarthShadow::FULL_UMBRA: + return "Full Umbral"; + case KSEarthShadow::FULL_PENUMBRA: + return "Full Penumbral"; + case KSEarthShadow::PARTIAL: + case KSEarthShadow::NONE: + return ""; + } + return ""; +} + +SkyObject *LunarEclipseEvent::getEclipsingObjectFromSkyComposite() +{ + return KStarsData::Instance()->skyComposite()->solarSystemComposite()->moon(); +} + +void LunarEclipseEvent::slotShowDetails() +{ + if(!m_details.available) { + LunarEclipseHandler handler; + GeoLocation loc = getGeolocation(); + handler.setGeoLocation(&loc); + handler.findEclipseDetails(this); + } +} Index: kstars/tools/ksconjunct.h =================================================================== --- kstars/tools/ksconjunct.h +++ kstars/tools/ksconjunct.h @@ -16,11 +16,7 @@ ***************************************************************************/ #pragma once - -#include "dms.h" - -#include -#include +#include "approachsolver.h" class GeoLocation; class KSPlanetBase; @@ -34,79 +30,32 @@ * algorithms required to find the time of closest approach in a given range of time. * * @author Akarsh Simha - * @version 1.0 + * @version 2.0 */ -class KSConjunct : public QObject +class KSConjunct : public ApproachSolver { - Q_OBJECT - - public: +Q_OBJECT +public: /** Constructor. Instantiates a KSNumbers for internal computations. */ KSConjunct(); - /** - * @short Sets the geographic location to compute conjunctions at - * - * @param geo Pointer to the GeoLocation object - */ - void setGeoLocation(GeoLocation *geo); + void setObject1(SkyObject_s &obj) { m_object1 = obj; } + void setObject2(KSPlanetBase_s &obj) { m_object2 = obj; } + void setOpposition(bool opposition) { m_opposition = opposition; } - /** - * @short Compute the closest approach of two planets in the given range - * - * @param Object1 A copy of the class corresponding to one of the two bodies - * @param Object2 A copy of the class corresponding to the other of the two bodies - * @param startJD Julian Day corresponding to start of the calculation period - * @param stopJD Julian Day corresponding to end of the calculation period - * @param maxSeparation Maximum separation between Object1 and Object2 - a measure - * how close the conjunction should be to be output. - * @param opposition A parameter to see if we are computing conjunction or opposition - * @return Hash containing julian days of close conjunctions against separation - */ - QMap findClosestApproach(SkyObject &Object1, KSPlanetBase &Object2, long double startJD, - long double stopJD, dms maxSeparation, bool _opposition = false); +signals: + void madeProgress(int); - signals: - void madeProgress(int progress); +protected: + double findInitialStep(long double startJD, long double stopJD) override; + void updatePositions(long double jd) override; - private: - /** - * @short Finds the angular distance between two solar system objects. - * - * @param jd Julian Day corresponding to the time of computation - * @param Object1 A pointer to the first solar system object - * @param Object2 A pointer to the second solar system object - * - * @return The angular distance between the two bodies. - */ - // TODO: Make pointers to Object1 and Object2 private objects instead of passing them to the methods again and again. - // Should improve performance, at least marginally. - dms findDistance(long double jd, SkyObject *Object1, KSPlanetBase *Object2); +private: + dms findDistance() override; - /** - * @short Compute the precise value of the extremum once the extremum has been detected. - * - * @param out A pointer to a QPair that stores the Julian Day and Separation corresponding to the extremum - * @param Object1 A pointer to the first solar system body - * @param Object2 A pointer to the second solar system body - * @param jd Julian day corresponding to the endpoint of the interval where extremum was detected. - * @param step The step in jd taken during computation earlier. (Defines the interval size) - * @param prevSign The previous sign of increment in moving from jd - step to jd - * - * @return true if the extremum is a minimum - */ - bool findPrecise(QPair *out, SkyObject *Object1, KSPlanetBase *Object2, long double jd, - double step, int prevSign); - /** - * @short Return the sign of an angle - * - * @param a The angle whose sign has to be returned - * - * @return (-1, 0, 1) if a.radians() is (-ve, zero, +ve) - */ - int sgn(dms a); - - bool opposition { false }; - GeoLocation *geoPlace { nullptr }; + SkyObject_s m_object1; + KSPlanetBase_s m_object2; + bool m_opposition { false }; }; + Index: kstars/tools/ksconjunct.cpp =================================================================== --- kstars/tools/ksconjunct.cpp +++ kstars/tools/ksconjunct.cpp @@ -19,206 +19,73 @@ #include "ksnumbers.h" #include "kstarsdata.h" -#include "skyobjects/ksplanet.h" -#include "skyobjects/ksplanetbase.h" #include "skyobjects/skyobject.h" +#include "skyobjects/ksplanetbase.h" #include -#include +KSConjunct::KSConjunct() : ApproachSolver () +{ + connect(this, &ApproachSolver::solverMadeProgress, this, &KSConjunct::madeProgress); +} -KSConjunct::KSConjunct() +dms KSConjunct::findDistance() { - geoPlace = KStarsData::Instance()->geo(); + dms dist = findSkyPointDistance(m_object1.get(), m_object2.get()); + if (m_opposition) + { + dist.setD(180 - dist.Degrees()); + } + + return dist; } -void KSConjunct::setGeoLocation(GeoLocation *geo) +void KSConjunct::updatePositions(long double jd) { - if (geo != nullptr) - geoPlace = geo; + KStarsDateTime t(jd); + KSNumbers num(jd); + + m_Earth.findPosition(&num); + CachingDms LST(getGeoLocation()->GSTtoLST(t.gst())); + + KSPlanetBase *p = dynamic_cast(m_object1.get()); + if (p) + p->findPosition(&num, getGeoLocation()->lat(), &LST, &m_Earth); else - geoPlace = KStarsData::Instance()->geo(); + m_object1->updateCoordsNow(&num); + + m_object2->findPosition(&num, getGeoLocation()->lat(), &LST, &m_Earth); } -QMap KSConjunct::findClosestApproach(SkyObject &Object1, KSPlanetBase &Object2, long double startJD, - long double stopJD, dms maxSeparation, bool _opposition) +double KSConjunct::findInitialStep(long double startJD, long double stopJD) { - QMap Separations; - QPair extremum; - dms Dist; - dms prevDist; - double step, step0; - int Sign, prevSign; - opposition = _opposition; - // qCDebug(KSTARS) << "Entered KSConjunct::findClosestApproach() with startJD = " << (double)startJD; - // qCDebug(KSTARS) << "Initial Positional Information: \n"; - // qCDebug(KSTARS) << Object1.name() << ": RA = " << Object1.ra() -> toHMSString() << "; Dec = " << Object1.dec() -> toDMSString() << "\n"; - // qCDebug(KSTARS) << Object2.name() << ": RA = " << Object2.ra() -> toHMSString() << "; Dec = " << Object2.dec() -> toDMSString() << "\n"; - prevSign = 0; - - step0 = - (stopJD - startJD) / 4.0; // I'm an idiot for having done this without having the lines that follow -- asimha + + double step0 = + double(stopJD - startJD) / 4.0; // I'm an idiot for having done this without having the lines that follow -- asimha // TODO: Work out a solid footing on which one can decide step0. -- asimha if (step0 > 24.8 * 365.25) // Sample pluto's orbit (248.09 years) at least 10 times. step0 = 24.8 * 365.25; // FIXME: This can be done better, but for now, I'm doing it the dumb way -- asimha - if (Object1.name() == i18n("Neptune") || Object2.name() == i18n("Neptune") || Object1.name() == i18n("Uranus") || - Object2.name() == i18n("Uranus")) + if (m_object1->name() == i18n("Neptune") || m_object2->name() == i18n("Neptune") || m_object1->name() == i18n("Uranus") || + m_object2->name() == i18n("Uranus")) if (step0 > 3652.5) step0 = 3652.5; - if (Object1.name() == i18n("Jupiter") || Object2.name() == i18n("Jupiter") || Object1.name() == i18n("Saturn") || - Object2.name() == i18n("Saturn")) + if (m_object1->name() == i18n("Jupiter") || m_object2->name() == i18n("Jupiter") || m_object1->name() == i18n("Saturn") || + m_object2->name() == i18n("Saturn")) if (step0 > 365.25) step0 = 365; - if (Object1.name() == i18n("Mars") || Object2.name() == i18n("Mars")) + if (m_object1->name() == i18n("Mars") || m_object2->name() == i18n("Mars")) if (step0 > 10.0) step0 = 10.0; - if (Object1.name() == i18n("Venus") || Object1.name() == i18n("Mercury") || Object2.name() == i18n("Mercury") || - Object2.name() == i18n("Venus")) + if (m_object1->name() == i18n("Venus") || m_object1->name() == i18n("Mercury") || m_object2->name() == i18n("Mercury") || + m_object2->name() == i18n("Venus")) if (step0 > 5.0) step0 = 5.0; - if (Object1.name() == "Moon" || Object2.name() == "Moon") + if (m_object1->name() == "Moon" || m_object2->name() == "Moon") if (step0 > 0.25) step0 = 0.25; - step = step0; - - // qCDebug(KSTARS) << "Initial Separation between " << Object1.name() << " and " << Object2.name() << " = " << (prevDist.toDMSString()); - - long double jd = startJD; - prevDist = findDistance(jd, &Object1, &Object2); - jd += step; - while (jd <= stopJD) - { - int progress = int(100.0 * (jd - startJD) / (stopJD - startJD)); - emit madeProgress(progress); - - Dist = findDistance(jd, &Object1, &Object2); - Sign = sgn(Dist - prevDist); - // qCDebug(KSTARS) << "Dist = " << Dist.toDMSString() << "; prevDist = " << prevDist.toDMSString() << "; Difference = " << (Dist.Degrees() - prevDist.Degrees()) << "; Step = " << step; - - //How close are we to a conjunction, and how fast are we approaching one? - double factor = fabs((Dist.Degrees() - prevDist.Degrees()) / Dist.Degrees()); - if (factor > 10.0) //let's go faster! - { - step = step0 * factor / 10.0; - } - else //slow down, we're getting close! - { - step = step0; - } - - if (Sign != prevSign && prevSign == -1) //all right, we may have just passed a conjunction - { - if (step > step0) //mini-loop to back up and make sure we're close enough - { - // qCDebug(KSTARS) << "Entering slow loop: " << endl; - jd -= step; - step = step0; - Sign = prevSign; - while (jd <= stopJD) - { - Dist = findDistance(jd, &Object1, &Object2); - Sign = sgn(Dist - prevDist); - // qCDebug(KSTARS) << "Dist=" << Dist.toDMSString() << "; prevDist=" << prevDist.toDMSString() << "; Diff=" << (Dist.Degrees() - prevDist.Degrees()) << "djd=" << (int)(jd - startJD); - if (Sign != prevSign) - break; - - prevDist = Dist; - prevSign = Sign; - jd += step; - } - } - - // qCDebug(KSTARS) << "Sign = " << Sign << " and " << "prevSign = " << prevSign << ": Entering findPrecise()\n"; - if (findPrecise(&extremum, &Object1, &Object2, jd, step, Sign)) - if (extremum.second.radians() < maxSeparation.radians()) - Separations.insert(extremum.first, extremum.second); - } - - prevDist = Dist; - prevSign = Sign; - jd += step; - } - - return Separations; -} - -dms KSConjunct::findDistance(long double jd, SkyObject *Object1, KSPlanetBase *Object2) -{ - KStarsDateTime t(jd); - KSNumbers num(jd); - dms dist; - - KSPlanet *m_Earth = new KSPlanet(I18N_NOOP("Earth"), QString(), QColor("white"), 12756.28 /*diameter in km*/); - m_Earth->findPosition(&num); - CachingDms LST(geoPlace->GSTtoLST(t.gst())); - - KSPlanetBase *p = dynamic_cast(Object1); - if (p) - p->findPosition(&num, geoPlace->lat(), &LST, m_Earth); - else - Object1->updateCoordsNow(&num); - - Object2->findPosition(&num, geoPlace->lat(), &LST, m_Earth); - dist.setRadians(Object1->angularDistanceTo(Object2).radians()); - if (opposition) - { - dist.setD(180 - dist.Degrees()); - } - return dist; -} - -bool KSConjunct::findPrecise(QPair *out, SkyObject *Object1, KSPlanetBase *Object2, long double jd, - double step, int prevSign) -{ - dms prevDist; - int Sign; - dms Dist; - - if (out == nullptr) - { - qCDebug(KSTARS) << "ERROR: Argument out to KSConjunct::findPrecise(...) was nullptr!"; - return false; - } - - prevDist = findDistance(jd, Object1, Object2); - - step = -step / 2.0; - prevSign = -prevSign; - - while (true) - { - jd += step; - Dist = findDistance(jd, Object1, Object2); - // qCDebug(KSTARS) << "Dist=" << Dist.toDMSString() << "; prevDist=" << prevDist.toDMSString() << "; Diff=" << (Dist.Degrees() - prevDist.Degrees()) << "step=" << step; - - if (fabs(step) < 1.0 / (24.0 * 60.0)) - { - out->first = jd - step / 2.0; - out->second = findDistance(jd - step / 2.0, Object1, Object2); - if (out->second.radians() < findDistance(jd - 5.0, Object1, Object2).radians()) - return true; - else - return false; - } - Sign = sgn(Dist - prevDist); - if (Sign != prevSign) - { - step = -step / 2.0; - Sign = -Sign; - } - prevSign = Sign; - prevDist = Dist; - } -} - -int KSConjunct::sgn(dms a) -{ - // Auxiliary function used by the KSConjunct::findClosestApproach(...) - // method and the KSConjunct::findPrecise(...) method - - return ((a.radians() > 0) ? 1 : ((a.radians() < 0) ? -1 : 0)); + return step0; }