File indexing completed on 2022-12-06 18:58:52

0001 /*
0002     SPDX-FileCopyrightText: 2001 Jason Harris <jharris@30doradus.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 // This file implements the class SkyMapDrawAbstract, and is almost
0008 // identical to the older skymapdraw.cpp file, written by Jason
0009 // Harris. Essentially, skymapdraw.cpp was renamed and modified.
0010 // -- asimha (2011)
0011 
0012 #include <QPainter>
0013 #include <QPixmap>
0014 
0015 #include "skymapdrawabstract.h"
0016 #include "skymap.h"
0017 #include "Options.h"
0018 #include "fov.h"
0019 #include "kstars.h"
0020 #include "kstarsdata.h"
0021 #include "ksnumbers.h"
0022 #include "ksutils.h"
0023 #include "skyobjects/skyobject.h"
0024 #include "skyobjects/catalogobject.h"
0025 #include "catalogsdb.h"
0026 #include "skyobjects/starobject.h"
0027 #include "skyobjects/ksplanetbase.h"
0028 #include "simclock.h"
0029 #include "observinglist.h"
0030 #include "skycomponents/constellationboundarylines.h"
0031 #include "skycomponents/skylabeler.h"
0032 #include "skycomponents/skymapcomposite.h"
0033 #include "skyqpainter.h"
0034 #include "projections/projector.h"
0035 #include "projections/lambertprojector.h"
0036 
0037 #include <config-kstars.h>
0038 
0039 #ifdef HAVE_INDI
0040 #include <basedevice.h>
0041 #include "indi/indilistener.h"
0042 #include "indi/driverinfo.h"
0043 #include "indi/indistd.h"
0044 #include "indi/indimount.h"
0045 #endif
0046 
0047 bool SkyMapDrawAbstract::m_DrawLock = false;
0048 
0049 SkyMapDrawAbstract::SkyMapDrawAbstract(SkyMap *sm) : m_KStarsData(KStarsData::Instance()), m_SkyMap(sm)
0050 {
0051     //m_fpstime.start();
0052     //m_framecount = 0;
0053 }
0054 
0055 void SkyMapDrawAbstract::drawOverlays(QPainter &p, bool drawFov)
0056 {
0057     if (!KStars::Instance())
0058         return;
0059 
0060     //draw labels
0061     SkyLabeler::Instance()->draw(p);
0062 
0063     if (drawFov)
0064     {
0065         //draw FOV symbol
0066         foreach (FOV *fov, m_KStarsData->getVisibleFOVs())
0067         {
0068             if (fov->lockCelestialPole())
0069             {
0070                 SkyPoint centerSkyPoint = SkyMap::Instance()->projector()->fromScreen(p.viewport().center(), KStarsData::Instance()->lst(),
0071                                           KStarsData::Instance()->geo()->lat());
0072                 QPointF screenSkyPoint = p.viewport().center();
0073                 double northRotation = SkyMap::Instance()->projector()->findNorthPA(&centerSkyPoint, screenSkyPoint.x(),
0074                                        screenSkyPoint.y());
0075                 fov->setCenter(centerSkyPoint);
0076                 fov->setNorthPA(northRotation);
0077             }
0078             fov->draw(p, Options::zoomFactor());
0079         }
0080     }
0081 
0082     drawSolverFOV(p);
0083 
0084     drawTelescopeSymbols(p);
0085 
0086     drawZoomBox(p);
0087 
0088     // FIXME: Maybe we should take care of this differently. Maybe
0089     // drawOverlays should remain in SkyMap, since it just calls
0090     // certain drawing functions which are implemented in
0091     // SkyMapDrawAbstract. Really, it doesn't draw anything on its
0092     // own.
0093     if (m_SkyMap->rulerMode)
0094     {
0095         m_SkyMap->updateAngleRuler();
0096         drawAngleRuler(p);
0097     }
0098 }
0099 
0100 void SkyMapDrawAbstract::drawAngleRuler(QPainter &p)
0101 {
0102     //FIXME use sky painter.
0103     p.setPen(QPen(m_KStarsData->colorScheme()->colorNamed("AngularRuler"), 3.0, Qt::DotLine));
0104     p.drawLine(
0105         m_SkyMap->m_proj->toScreen(m_SkyMap->AngularRuler.point(
0106                                        0)), // FIXME: More ugliness. m_proj should probably be a single-instance class, or we should have our own instance etc.
0107         m_SkyMap->m_proj->toScreen(m_SkyMap->AngularRuler.point(
0108                                        1))); // FIXME: Again, AngularRuler should be something better -- maybe a class in itself. After all it's used for more than one thing after we integrate the StarHop feature.
0109 }
0110 
0111 void SkyMapDrawAbstract::drawZoomBox(QPainter &p)
0112 {
0113     //draw the manual zoom-box, if it exists
0114     if (m_SkyMap->ZoomRect.isValid())
0115     {
0116         p.setPen(QPen(Qt::white, 1.0, Qt::DotLine));
0117         p.drawRect(m_SkyMap->ZoomRect.x(), m_SkyMap->ZoomRect.y(), m_SkyMap->ZoomRect.width(),
0118                    m_SkyMap->ZoomRect.height());
0119     }
0120 }
0121 
0122 void SkyMapDrawAbstract::drawObjectLabels(QList<SkyObject *> &labelObjects)
0123 {
0124     bool checkSlewing =
0125         (m_SkyMap->slewing || (m_SkyMap->clockSlewing && m_KStarsData->clock()->isActive())) && Options::hideOnSlew();
0126     if (checkSlewing && Options::hideLabels())
0127         return;
0128 
0129     SkyLabeler *skyLabeler = SkyLabeler::Instance();
0130     skyLabeler->resetFont(); // use the zoom dependent font
0131 
0132     skyLabeler->setPen(m_KStarsData->colorScheme()->colorNamed("UserLabelColor"));
0133 
0134     bool drawPlanets   = Options::showSolarSystem() && !(checkSlewing && Options::hidePlanets());
0135     bool drawComets    = drawPlanets && Options::showComets();
0136     bool drawAsteroids = drawPlanets && Options::showAsteroids();
0137     bool drawOther      = Options::showDeepSky() && Options::showOther() && !(checkSlewing && Options::hideOther());
0138     bool drawStars      = Options::showStars();
0139     bool hideFaintStars = checkSlewing && Options::hideStars();
0140 
0141     //Attach a label to the centered object
0142     if (m_SkyMap->focusObject() != nullptr && Options::useAutoLabel())
0143     {
0144         QPointF o =
0145             m_SkyMap->m_proj->toScreen(m_SkyMap->focusObject()); // FIXME: Same thing. m_proj should be accessible here.
0146         skyLabeler->drawNameLabel(m_SkyMap->focusObject(), o);
0147     }
0148 
0149     foreach (SkyObject *obj, labelObjects)
0150     {
0151         //Only draw an attached label if the object is being drawn to the map
0152         //reproducing logic from other draw funcs here...not an optimal solution
0153         if (obj->type() == SkyObject::STAR || obj->type() == SkyObject::CATALOG_STAR ||
0154                 obj->type() == SkyObject::MULT_STAR)
0155         {
0156             if (!drawStars)
0157                 continue;
0158             //            if ( obj->mag() > Options::magLimitDrawStar() ) continue;
0159             if (hideFaintStars && obj->mag() > Options::magLimitHideStar())
0160                 continue;
0161         }
0162         if (obj->type() == SkyObject::PLANET)
0163         {
0164             if (!drawPlanets)
0165                 continue;
0166             if (obj->name() == i18n("Sun") && !Options::showSun())
0167                 continue;
0168             if (obj->name() == i18n("Mercury") && !Options::showMercury())
0169                 continue;
0170             if (obj->name() == i18n("Venus") && !Options::showVenus())
0171                 continue;
0172             if (obj->name() == i18n("Moon") && !Options::showMoon())
0173                 continue;
0174             if (obj->name() == i18n("Mars") && !Options::showMars())
0175                 continue;
0176             if (obj->name() == i18n("Jupiter") && !Options::showJupiter())
0177                 continue;
0178             if (obj->name() == i18n("Saturn") && !Options::showSaturn())
0179                 continue;
0180             if (obj->name() == i18n("Uranus") && !Options::showUranus())
0181                 continue;
0182             if (obj->name() == i18n("Neptune") && !Options::showNeptune())
0183                 continue;
0184             //if ( obj->name() == i18n( "Pluto" ) && ! Options::showPluto() ) continue;
0185         }
0186         if ((obj->type() >= SkyObject::OPEN_CLUSTER && obj->type() <= SkyObject::GALAXY) ||
0187                 (obj->type() >= SkyObject::ASTERISM && obj->type() <= SkyObject::QUASAR) ||
0188                 (obj->type() == SkyObject::RADIO_SOURCE))
0189         {
0190             if (((CatalogObject *)obj)->getCatalog().id == -1 && !drawOther)
0191                 continue;
0192         }
0193         if (obj->type() == SkyObject::COMET && !drawComets)
0194             continue;
0195         if (obj->type() == SkyObject::ASTEROID && !drawAsteroids)
0196             continue;
0197 
0198         if (!m_SkyMap->m_proj->checkVisibility(obj))
0199             continue; // FIXME: m_proj should be a member of this class.
0200         QPointF o = m_SkyMap->m_proj->toScreen(obj);
0201         if (!m_SkyMap->m_proj->onScreen(o))
0202             continue;
0203 
0204         skyLabeler->drawNameLabel(obj, o);
0205     }
0206 
0207     skyLabeler->useStdFont(); // use the StdFont for the guides.
0208 }
0209 
0210 void SkyMapDrawAbstract::drawSolverFOV(QPainter &psky)
0211 {
0212     Q_UNUSED(psky)
0213 
0214 #ifdef HAVE_INDI
0215 
0216     for (auto oneFOV : KStarsData::Instance()->getTransientFOVs())
0217     {
0218         QVariant visible = oneFOV->property("visible");
0219         if (visible.isNull() || visible.toBool() == false)
0220             continue;
0221 
0222         if (oneFOV->objectName() == "sensor_fov")
0223         {
0224             oneFOV->setColor(KStars::Instance()->data()->colorScheme()->colorNamed("SensorFOVColor").name());
0225             SkyPoint centerSkyPoint = SkyMap::Instance()->projector()->fromScreen(psky.viewport().center(),
0226                                       KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat());
0227             QPointF screenSkyPoint = psky.viewport().center();
0228             double northRotation = SkyMap::Instance()->projector()->findNorthPA(&centerSkyPoint, screenSkyPoint.x(),
0229                                    screenSkyPoint.y());
0230             oneFOV->setCenter(centerSkyPoint);
0231             oneFOV->setNorthPA(northRotation);
0232             oneFOV->draw(psky, Options::zoomFactor());
0233         }
0234         else if (oneFOV->objectName() == "solver_fov")
0235         {
0236             bool isVisible = false;
0237             SkyPoint p = oneFOV->center();
0238             if (std::isnan(p.ra().Degrees()))
0239                 continue;
0240 
0241             p.EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat());
0242             QPointF point        = SkyMap::Instance()->projector()->toScreen(&p, true, &isVisible);
0243             double northRotation = SkyMap::Instance()->projector()->findNorthPA(&p, point.x(), point.y());
0244             oneFOV->setNorthPA(northRotation);
0245             oneFOV->draw(psky, Options::zoomFactor());
0246         }
0247     }
0248 #endif
0249 }
0250 
0251 void SkyMapDrawAbstract::drawTelescopeSymbols(QPainter &psky)
0252 {
0253     Q_UNUSED(psky)
0254 
0255 #ifdef HAVE_INDI
0256     if (!Options::showTargetCrosshair())
0257         return;
0258 
0259     if (INDIListener::Instance()->size() == 0)
0260         return;
0261     SkyPoint indi_sp;
0262 
0263     psky.setPen(QPen(QColor(m_KStarsData->colorScheme()->colorNamed("TargetColor"))));
0264     psky.setBrush(Qt::NoBrush);
0265     float pxperdegree = Options::zoomFactor() / 57.3;
0266 
0267     for (auto &oneDevice : INDIListener::devices())
0268     {
0269         if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE) || oneDevice->isConnected() == false)
0270             continue;
0271 
0272         auto mount = oneDevice->getMount();
0273         if (!mount)
0274             continue;
0275 
0276         auto coordNP = mount->currentCoordinates();
0277 
0278         QPointF P = m_SkyMap->m_proj->toScreen(&coordNP);
0279         if (Options::useAntialias())
0280         {
0281             float s1 = 0.5 * pxperdegree;
0282             float s2 = pxperdegree;
0283             float s3 = 2.0 * pxperdegree;
0284 
0285             float x0 = P.x();
0286             float y0 = P.y();
0287             float x1 = x0 - 0.5 * s1;
0288             float y1 = y0 - 0.5 * s1;
0289             float x2 = x0 - 0.5 * s2;
0290             float y2 = y0 - 0.5 * s2;
0291             float x3 = x0 - 0.5 * s3;
0292             float y3 = y0 - 0.5 * s3;
0293 
0294             //Draw radial lines
0295             psky.drawLine(QPointF(x1, y0), QPointF(x3, y0));
0296             psky.drawLine(QPointF(x0 + s2, y0), QPointF(x0 + 0.5 * s1, y0));
0297             psky.drawLine(QPointF(x0, y1), QPointF(x0, y3));
0298             psky.drawLine(QPointF(x0, y0 + 0.5 * s1), QPointF(x0, y0 + s2));
0299             //Draw circles at 0.5 & 1 degrees
0300             psky.drawEllipse(QRectF(x1, y1, s1, s1));
0301             psky.drawEllipse(QRectF(x2, y2, s2, s2));
0302 
0303             psky.drawText(QPointF(x0 + s2 + 2., y0), mount->getDeviceName());
0304         }
0305         else
0306         {
0307             int s1 = int(0.5 * pxperdegree);
0308             int s2 = int(pxperdegree);
0309             int s3 = int(2.0 * pxperdegree);
0310 
0311             int x0 = int(P.x());
0312             int y0 = int(P.y());
0313             int x1 = x0 - s1 / 2;
0314             int y1 = y0 - s1 / 2;
0315             int x2 = x0 - s2 / 2;
0316             int y2 = y0 - s2 / 2;
0317             int x3 = x0 - s3 / 2;
0318             int y3 = y0 - s3 / 2;
0319 
0320             //Draw radial lines
0321             psky.drawLine(QPoint(x1, y0), QPoint(x3, y0));
0322             psky.drawLine(QPoint(x0 + s2, y0), QPoint(x0 + s1 / 2, y0));
0323             psky.drawLine(QPoint(x0, y1), QPoint(x0, y3));
0324             psky.drawLine(QPoint(x0, y0 + s1 / 2), QPoint(x0, y0 + s2));
0325             //Draw circles at 0.5 & 1 degrees
0326             psky.drawEllipse(QRect(x1, y1, s1, s1));
0327             psky.drawEllipse(QRect(x2, y2, s2, s2));
0328 
0329             psky.drawText(QPoint(x0 + s2 + 2, y0), mount->getDeviceName());
0330         }
0331     }
0332 #endif
0333 }
0334 
0335 void SkyMapDrawAbstract::exportSkyImage(QPaintDevice *pd, bool scale)
0336 {
0337     SkyQPainter p(m_SkyMap, pd);
0338     p.begin();
0339     p.setRenderHint(QPainter::SmoothPixmapTransform, true);
0340 
0341     exportSkyImage(&p, scale);
0342 
0343     p.end();
0344 }
0345 
0346 void SkyMapDrawAbstract::exportSkyImage(SkyQPainter *painter, bool scale)
0347 {
0348     bool vectorStarState;
0349     vectorStarState = painter->getVectorStars();
0350     painter->setVectorStars(
0351         true); // Since we are exporting an image, we may use vector stars without worrying about time
0352     painter->setRenderHint(QPainter::Antialiasing, Options::useAntialias());
0353 
0354     if (scale)
0355     {
0356         //scale sky image to fit paint device
0357         qDebug() << Q_FUNC_INFO << "Scaling true while exporting Sky Image";
0358         double xscale = double(painter->device()->width()) / double(m_SkyMap->width());
0359         double yscale = double(painter->device()->height()) / double(m_SkyMap->height());
0360         double scale  = qMin(xscale, yscale);
0361         qDebug() << Q_FUNC_INFO << "xscale: " << xscale << "yscale: " << yscale << "chosen scale: " << scale;
0362         painter->scale(scale, scale);
0363     }
0364 
0365     painter->drawSkyBackground();
0366     m_KStarsData->skyComposite()->draw(painter);
0367     drawOverlays(*painter);
0368     painter->setVectorStars(vectorStarState); // Restore the state of the painter
0369 }
0370 
0371 /* JM 2016-05-03: Not needed since we're not using OpenGL for now
0372  * void SkyMapDrawAbstract::calculateFPS()
0373 {
0374     if(m_framecount == 25) {
0375         //float sec = m_fpstime.elapsed()/1000.;
0376         // qDebug() << Q_FUNC_INFO << "FPS " << m_framecount/sec;
0377         m_framecount = 0;
0378         m_fpstime.restart();
0379     }
0380     ++m_framecount;
0381 }*/
0382 
0383 void SkyMapDrawAbstract::setDrawLock(bool state)
0384 {
0385     m_DrawLock = state;
0386 }