File indexing completed on 2024-12-01 09:41:29
0001 /* 0002 SPDX-FileCopyrightText: 2016 Artem Fedoskin <afedoskin3@gmail.com> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "skymaplite.h" 0007 #include "kstarsdata.h" 0008 #include "kstarslite.h" 0009 0010 #include "indi/inditelescopelite.h" 0011 #include "indi/clientmanagerlite.h" 0012 #include "kstarslite/skyitems/telescopesymbolsitem.h" 0013 0014 #include "projections/projector.h" 0015 #include "projections/lambertprojector.h" 0016 #include "projections/gnomonicprojector.h" 0017 #include "projections/stereographicprojector.h" 0018 #include "projections/orthographicprojector.h" 0019 #include "projections/azimuthalequidistantprojector.h" 0020 #include "projections/equirectangularprojector.h" 0021 0022 #include "kstarslite/skypointlite.h" 0023 #include "kstarslite/skyobjectlite.h" 0024 0025 #include "skylabeler.h" 0026 #include "Options.h" 0027 #include "skymesh.h" 0028 0029 #include "kstarslite/skyitems/rootnode.h" 0030 #include "kstarslite/skyitems/skynodes/skynode.h" 0031 0032 #include "ksplanetbase.h" 0033 #include "ksutils.h" 0034 0035 #include <QSGSimpleRectNode> 0036 //#include <QSGNode> 0037 #include <QBitmap> 0038 #include <QSGTexture> 0039 #include <QQuickWindow> 0040 #include <QLinkedList> 0041 #include <QQmlContext> 0042 #include <QScreen> 0043 0044 #include "kstarslite/deviceorientation.h" 0045 0046 namespace 0047 { 0048 // Draw bitmap for zoom cursor. Width is size of pen to draw with. 0049 QBitmap zoomCursorBitmap(int width) 0050 { 0051 QBitmap b(32, 32); 0052 b.fill(Qt::color0); 0053 int mx = 16, my = 16; 0054 // Begin drawing 0055 QPainter p; 0056 p.begin(&b); 0057 p.setPen(QPen(Qt::color1, width)); 0058 p.drawEllipse(mx - 7, my - 7, 14, 14); 0059 p.drawLine(mx + 5, my + 5, mx + 11, my + 11); 0060 p.end(); 0061 return b; 0062 } 0063 0064 // Draw bitmap for default cursor. Width is size of pen to draw with. 0065 QBitmap defaultCursorBitmap(int width) 0066 { 0067 QBitmap b(32, 32); 0068 b.fill(Qt::color0); 0069 int mx = 16, my = 16; 0070 // Begin drawing 0071 QPainter p; 0072 p.begin(&b); 0073 p.setPen(QPen(Qt::color1, width)); 0074 // 1. diagonal 0075 p.drawLine(mx - 2, my - 2, mx - 8, mx - 8); 0076 p.drawLine(mx + 2, my + 2, mx + 8, mx + 8); 0077 // 2. diagonal 0078 p.drawLine(mx - 2, my + 2, mx - 8, mx + 8); 0079 p.drawLine(mx + 2, my - 2, mx + 8, mx - 8); 0080 p.end(); 0081 return b; 0082 } 0083 } 0084 0085 SkyMapLite *SkyMapLite::pinstance = nullptr; 0086 0087 RootNode *SkyMapLite::m_rootNode = nullptr; 0088 0089 int SkyMapLite::starColorMode = 0; 0090 0091 SkyMapLite::SkyMapLite() 0092 : data(KStarsData::Instance()) 0093 #if defined(Q_OS_ANDROID) 0094 , 0095 m_deviceOrientation(new DeviceOrientation(this)) 0096 #endif 0097 { 0098 setAcceptHoverEvents(true); 0099 setAcceptedMouseButtons(Qt::AllButtons); 0100 setFlag(ItemHasContents, true); 0101 0102 m_rootNode = nullptr; 0103 m_magLim = 2.222 * log10(static_cast<double>(Options::starDensity())) + 0.35; 0104 0105 setSlewing(false); 0106 0107 m_ClickedObjectLite = new SkyObjectLite; 0108 m_ClickedPointLite = new SkyPointLite; 0109 0110 qmlRegisterType<SkyObjectLite>("KStarsLite", 1, 0, "SkyObjectLite"); 0111 qmlRegisterType<SkyPointLite>("KStarsLite", 1, 0, "SkyPointLite"); 0112 0113 m_tapBeganTimer.setSingleShot(true); 0114 0115 setupProjector(); 0116 0117 // Set pinstance to yourself 0118 pinstance = this; 0119 0120 connect(this, SIGNAL(destinationChanged()), this, SLOT(slewFocus())); 0121 connect(KStarsData::Instance(), SIGNAL(skyUpdate(bool)), this, SLOT(slotUpdateSky(bool))); 0122 0123 ClientManagerLite *clientMng = KStarsLite::Instance()->clientManagerLite(); 0124 0125 connect(clientMng, &ClientManagerLite::telescopeAdded, 0126 [this](TelescopeLite * newTelescope) 0127 { 0128 this->m_newTelescopes.append(newTelescope->getDevice()); 0129 }); 0130 connect(clientMng, &ClientManagerLite::telescopeRemoved, 0131 [this](TelescopeLite * newTelescope) 0132 { 0133 this->m_delTelescopes.append(newTelescope->getDevice()); 0134 }); 0135 #if defined(Q_OS_ANDROID) 0136 //Automatic mode 0137 automaticModeTimer.setInterval(5); 0138 connect(&automaticModeTimer, SIGNAL(timeout()), this, SLOT(updateAutomaticMode())); 0139 setAutomaticMode(false); 0140 #endif 0141 } 0142 0143 QSGNode *SkyMapLite::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) 0144 { 0145 Q_UNUSED(updatePaintNodeData); 0146 RootNode *n = static_cast<RootNode *>(oldNode); 0147 0148 /* This code deletes all nodes that are representing dynamic stars and not needed anymore (under construction) */ 0149 //qDeleteAll(m_deleteNodes); 0150 //m_deleteNodes.clear(); 0151 0152 if (m_loadingFinished && isInitialized) 0153 { 0154 if (!n) 0155 { 0156 n = new RootNode(); 0157 m_rootNode = n; 0158 } 0159 /** Add or delete telescope crosshairs **/ 0160 if (m_newTelescopes.count() > 0) 0161 { 0162 foreach (INDI::BaseDevice *telescope, m_newTelescopes) 0163 { 0164 n->telescopeSymbolsItem()->addTelescope(telescope); 0165 } 0166 m_newTelescopes.clear(); 0167 } 0168 0169 if (m_delTelescopes.count() > 0) 0170 { 0171 foreach (INDI::BaseDevice *telescope, m_delTelescopes) 0172 { 0173 n->telescopeSymbolsItem()->removeTelescope(telescope); 0174 } 0175 m_delTelescopes.clear(); 0176 } 0177 //Notify RootNode that textures for point node should be recreated 0178 n->update(clearTextures); 0179 clearTextures = false; 0180 } 0181 0182 //Memory Leaks test 0183 /*if(m_loadingFinished) { 0184 if(!n) { 0185 n = new RootNode(); 0186 } 0187 n->testLeakAdd(); 0188 n->update(); 0189 m_loadingFinished = false; 0190 } else { 0191 if (n) { 0192 n->testLeakDelete(); 0193 } 0194 m_loadingFinished = true; 0195 }*/ 0196 return n; 0197 } 0198 0199 double SkyMapLite::deleteLimit() 0200 { 0201 double lim = (MAXZOOM / MINZOOM) / sqrt(Options::zoomFactor()) / 3; //(MAXZOOM/MINZOOM - Options::zoomFactor())/130; 0202 return lim; 0203 } 0204 0205 void SkyMapLite::deleteSkyNode(SkyNode *skyNode) 0206 { 0207 m_deleteNodes.append(skyNode); 0208 } 0209 0210 QSGTexture *SkyMapLite::getCachedTexture(int size, char spType) 0211 { 0212 return textureCache[harvardToIndex(spType)][size]; 0213 } 0214 0215 SkyMapLite *SkyMapLite::createInstance() 0216 { 0217 delete pinstance; 0218 pinstance = new SkyMapLite(); 0219 return pinstance; 0220 } 0221 0222 void SkyMapLite::initialize(QQuickItem *parent) 0223 { 0224 if (parent) 0225 { 0226 setParentItem(parent); 0227 // Whenever the wrapper's(parent) dimensions changed, change SkyMapLite too 0228 connect(parent, &QQuickItem::widthChanged, this, &SkyMapLite::resizeItem); 0229 connect(parent, &QQuickItem::heightChanged, this, &SkyMapLite::resizeItem); 0230 0231 isInitialized = true; 0232 } 0233 0234 resizeItem(); /* Set initial size pf SkyMapLite. Without it on Android SkyMapLite is 0235 not displayed until screen orientation is not changed*/ 0236 0237 //Initialize images for stars 0238 initStarImages(); 0239 } 0240 0241 SkyMapLite::~SkyMapLite() 0242 { 0243 // Delete image cache 0244 for (auto &imgCache : imageCache) 0245 qDeleteAll(imgCache); 0246 0247 // Delete textures generated from image cache 0248 for (auto &tCache : textureCache) 0249 qDeleteAll(tCache); 0250 } 0251 0252 void SkyMapLite::setFocus(SkyPoint *p) 0253 { 0254 setFocus(p->ra(), p->dec()); 0255 } 0256 0257 void SkyMapLite::setFocus(const dms &ra, const dms &dec) 0258 { 0259 Options::setFocusRA(ra.Hours()); 0260 Options::setFocusDec(dec.Degrees()); 0261 0262 focus()->set(ra, dec); 0263 focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0264 } 0265 0266 void SkyMapLite::setFocusAltAz(const dms &alt, const dms &az) 0267 { 0268 Options::setFocusRA(focus()->ra().Hours()); 0269 Options::setFocusDec(focus()->dec().Degrees()); 0270 focus()->setAlt(alt); 0271 focus()->setAz(az); 0272 focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat()); 0273 0274 setSlewing(false); 0275 forceUpdate(); //need a total update, or slewing with the arrow keys doesn't work. 0276 } 0277 0278 void SkyMapLite::setDestination(const SkyPoint &p) 0279 { 0280 setDestination(p.ra(), p.dec()); 0281 } 0282 0283 void SkyMapLite::setDestination(const dms &ra, const dms &dec) 0284 { 0285 destination()->set(ra, dec); 0286 destination()->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0287 emit destinationChanged(); 0288 } 0289 0290 void SkyMapLite::setDestinationAltAz(const dms &alt, const dms &az, bool altIsRefracted) 0291 { 0292 if (altIsRefracted) 0293 { 0294 // The alt in the SkyPoint is always actual, not apparent 0295 destination()->setAlt(SkyPoint::unrefract(alt)); 0296 } 0297 else 0298 { 0299 destination()->setAlt(alt); 0300 } 0301 destination()->setAz(az); 0302 destination()->HorizontalToEquatorial(data->lst(), data->geo()->lat()); 0303 emit destinationChanged(); 0304 } 0305 0306 void SkyMapLite::setClickedPoint(SkyPoint *f) 0307 { 0308 ClickedPoint = *f; 0309 m_ClickedPointLite->setPoint(f); 0310 } 0311 0312 void SkyMapLite::setClickedObject(SkyObject *o) 0313 { 0314 ClickedObject = o; 0315 m_ClickedObjectLite->setObject(o); 0316 } 0317 0318 void SkyMapLite::setFocusObject(SkyObject *o) 0319 { 0320 FocusObject = o; 0321 if (FocusObject) 0322 Options::setFocusObject(FocusObject->name()); 0323 else 0324 Options::setFocusObject(i18n("nothing")); 0325 } 0326 0327 void SkyMapLite::slotCenter() 0328 { 0329 /*KStars* kstars = KStars::Instance(); 0330 TrailObject* trailObj = dynamic_cast<TrailObject*>( focusObject() );*/ 0331 0332 setFocusPoint(clickedPoint()); 0333 if (Options::useAltAz()) 0334 { 0335 focusPoint()->updateCoords(data->updateNum(), true, data->geo()->lat(), data->lst(), false); 0336 focusPoint()->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0337 } 0338 else 0339 { 0340 focusPoint()->updateCoords(data->updateNum(), true, data->geo()->lat(), data->lst(), false); 0341 } 0342 qDebug() << "Centering on " << focusPoint()->ra().toHMSString() << " " << focusPoint()->dec().toDMSString(); 0343 0344 //clear the planet trail of old focusObject, if it was temporary 0345 /*if( trailObj && data->temporaryTrail ) { 0346 trailObj->clearTrail(); 0347 data->temporaryTrail = false; 0348 }*/ 0349 0350 //If the requested object is below the opaque horizon, issue a warning message 0351 //(unless user is already pointed below the horizon) 0352 if (Options::useAltAz() && Options::showGround() && focus()->alt().Degrees() > SkyPoint::altCrit && 0353 focusPoint()->alt().Degrees() <= SkyPoint::altCrit) 0354 { 0355 QString caption = i18n("Requested Position Below Horizon"); 0356 QString message = i18n("The requested position is below the horizon.\nWould you like to go there anyway?"); 0357 /*if ( KMessageBox::warningYesNo( this, message, caption, 0358 KGuiItem(i18n("Go Anyway")), KGuiItem(i18n("Keep Position")), "dag_focus_below_horiz" )==KMessageBox::No ) { 0359 setClickedObject( nullptr ); 0360 setFocusObject( nullptr ); 0361 Options::setIsTracking( false ); 0362 0363 return; 0364 }*/ 0365 } 0366 0367 //set FocusObject before slewing. Otherwise, KStarsData::updateTime() can reset 0368 //destination to previous object... 0369 setFocusObject(ClickedObject); 0370 Options::setIsTracking(true); 0371 /*if ( kstars ) { 0372 kstars->actionCollection()->action("track_object")->setIcon( QIcon::fromTheme("document-encrypt") ); 0373 kstars->actionCollection()->action("track_object")->setText( i18n( "Stop &Tracking" ) ); 0374 }*/ 0375 0376 //If focusObject is a SS body and doesn't already have a trail, set the temporaryTrail 0377 0378 /*if( Options::useAutoTrail() && trailObj && trailObj->hasTrail() ) { 0379 trailObj->addToTrail(); 0380 data->temporaryTrail = true; 0381 }*/ 0382 0383 //update the destination to the selected coordinates 0384 if (Options::useAltAz()) 0385 { 0386 setDestinationAltAz(focusPoint()->alt(), focusPoint()->az(), false); 0387 } 0388 else 0389 { 0390 setDestination(*focusPoint()); 0391 } 0392 0393 focusPoint()->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0394 0395 //display coordinates in statusBar 0396 emit mousePointChanged(focusPoint()); 0397 //showFocusCoords(); //update FocusBox 0398 //Lock center so that user could only zoom on touch-enabled devices 0399 } 0400 0401 void SkyMapLite::slewFocus() 0402 { 0403 //Don't slew if the mouse button is pressed 0404 //Also, no animated slews if the Manual Clock is active 0405 //08/2002: added possibility for one-time skipping of slew with snapNextFocus 0406 if (!mouseButtonDown) 0407 { 0408 bool goSlew = (Options::useAnimatedSlewing() && !data->snapNextFocus()) && 0409 !(data->clock()->isManualMode() && data->clock()->isActive()); 0410 if (goSlew) 0411 { 0412 double dX, dY; 0413 double maxstep = 10.0; 0414 if (Options::useAltAz()) 0415 { 0416 dX = destination()->az().Degrees() - focus()->az().Degrees(); 0417 dY = destination()->alt().Degrees() - focus()->alt().Degrees(); 0418 } 0419 else 0420 { 0421 dX = destination()->ra().Degrees() - focus()->ra().Degrees(); 0422 dY = destination()->dec().Degrees() - focus()->dec().Degrees(); 0423 } 0424 0425 //switch directions to go the short way around the celestial sphere, if necessary. 0426 dX = KSUtils::reduceAngle(dX, -180.0, 180.0); 0427 0428 double r0 = sqrt(dX * dX + dY * dY); 0429 if (r0 < 20.0) //smaller slews have smaller maxstep 0430 { 0431 maxstep *= (10.0 + 0.5 * r0) / 20.0; 0432 } 0433 double step = 0.1; 0434 double r = r0; 0435 while (r > step) 0436 { 0437 //DEBUG 0438 //qDebug() << step << ": " << r << ": " << r0 << endl; 0439 double fX = dX / r; 0440 double fY = dY / r; 0441 0442 if (Options::useAltAz()) 0443 { 0444 focus()->setAlt(focus()->alt().Degrees() + fY * step); 0445 focus()->setAz(dms(focus()->az().Degrees() + fX * step).reduce()); 0446 focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat()); 0447 } 0448 else 0449 { 0450 fX = fX / 15.; //convert RA degrees to hours 0451 SkyPoint newFocus(focus()->ra().Hours() + fX * step, focus()->dec().Degrees() + fY * step); 0452 setFocus(&newFocus); 0453 focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0454 } 0455 0456 setSlewing(true); 0457 0458 forceUpdate(); 0459 qApp->processEvents(); //keep up with other stuff 0460 0461 if (Options::useAltAz()) 0462 { 0463 dX = destination()->az().Degrees() - focus()->az().Degrees(); 0464 dY = destination()->alt().Degrees() - focus()->alt().Degrees(); 0465 } 0466 else 0467 { 0468 dX = destination()->ra().Degrees() - focus()->ra().Degrees(); 0469 dY = destination()->dec().Degrees() - focus()->dec().Degrees(); 0470 } 0471 0472 //switch directions to go the short way around the celestial sphere, if necessary. 0473 dX = KSUtils::reduceAngle(dX, -180.0, 180.0); 0474 r = sqrt(dX * dX + dY * dY); 0475 0476 //Modify step according to a cosine-shaped profile 0477 //centered on the midpoint of the slew 0478 //NOTE: don't allow the full range from -PI/2 to PI/2 0479 //because the slew will never reach the destination as 0480 //the speed approaches zero at the end! 0481 double t = dms::PI * (r - 0.5 * r0) / (1.05 * r0); 0482 step = cos(t) * maxstep; 0483 } 0484 } 0485 0486 //Either useAnimatedSlewing==false, or we have slewed, and are within one step of destination 0487 //set focus=destination. 0488 if (Options::useAltAz()) 0489 { 0490 setFocusAltAz(destination()->alt(), destination()->az()); 0491 focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat()); 0492 } 0493 else 0494 { 0495 setFocus(destination()); 0496 focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0497 } 0498 0499 setSlewing(false); 0500 0501 //Turn off snapNextFocus, we only want it to happen once 0502 if (data->snapNextFocus()) 0503 { 0504 data->setSnapNextFocus(false); 0505 } 0506 0507 //Start the HoverTimer. if the user leaves the mouse in place after a slew, 0508 //we want to attach a label to the nearest object. 0509 if (Options::useHoverLabel()) 0510 m_HoverTimer.start(HOVER_INTERVAL); 0511 0512 forceUpdate(); 0513 } 0514 } 0515 0516 void SkyMapLite::slotClockSlewing() 0517 { 0518 //If the current timescale exceeds slewTimeScale, set clockSlewing=true, and stop the clock. 0519 if ((fabs(data->clock()->scale()) > Options::slewTimeScale()) ^ clockSlewing) 0520 { 0521 data->clock()->setManualMode(!clockSlewing); 0522 clockSlewing = !clockSlewing; 0523 // don't change automatically the DST status 0524 KStarsLite *kstars = KStarsLite::Instance(); 0525 if (kstars) 0526 kstars->updateTime(false); 0527 } 0528 } 0529 0530 /*void SkyMapLite::updateFocus() { 0531 if( slewing ) 0532 return; 0533 0534 //Tracking on an object 0535 if ( Options::isTracking() && focusObject() != nullptr ) { 0536 if ( Options::useAltAz() ) { 0537 //Tracking any object in Alt/Az mode requires focus updates 0538 focusObject()->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0539 setFocusAltAz( focusObject()->alt(), focusObject()->az() ); 0540 focus()->HorizontalToEquatorial( data->lst(), data->geo()->lat() ); 0541 setDestination( *focus() ); 0542 } else { 0543 //Tracking in equatorial coords 0544 setFocus( focusObject() ); 0545 focus()->EquatorialToHorizontal( data->lst(), data->geo()->lat() ); 0546 setDestination( *focus() ); 0547 } 0548 0549 //Tracking on empty sky 0550 } else if ( Options::isTracking() && focusPoint() != nullptr ) { 0551 if ( Options::useAltAz() ) { 0552 //Tracking on empty sky in Alt/Az mode 0553 setFocus( focusPoint() ); 0554 focus()->EquatorialToHorizontal( data->lst(), data->geo()->lat() ); 0555 setDestination( *focus() ); 0556 } 0557 0558 // Not tracking and not slewing, let sky drift by 0559 // This means that horizontal coordinates are constant. 0560 } else { 0561 focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat() ); 0562 } 0563 }*/ 0564 0565 void SkyMapLite::resizeItem() 0566 { 0567 if (parentItem()) 0568 { 0569 setWidth(parentItem()->width()); 0570 setHeight(parentItem()->height()); 0571 } 0572 forceUpdate(); 0573 } 0574 0575 void SkyMapLite::slotZoomIn() 0576 { 0577 setZoomFactor(Options::zoomFactor() * DZOOM); 0578 } 0579 0580 void SkyMapLite::slotZoomOut() 0581 { 0582 setZoomFactor(Options::zoomFactor() / DZOOM); 0583 } 0584 0585 void SkyMapLite::slotZoomDefault() 0586 { 0587 setZoomFactor(DEFAULTZOOM); 0588 } 0589 0590 void SkyMapLite::slotSelectObject(SkyObject *skyObj) 0591 { 0592 ClickedPoint = *skyObj; 0593 ClickedObject = skyObj; 0594 /*if ( Options::useAltAz() ) { 0595 setDestinationAltAz( skyObj->altRefracted(), skyObj->az() ); 0596 } else { 0597 setDestination( *skyObj ); 0598 }*/ 0599 //Update selected SkyObject (used in FindDialog, DetailDialog) 0600 m_ClickedObjectLite->setObject(skyObj); 0601 emit objectLiteChanged(); 0602 slotCenter(); 0603 } 0604 0605 void SkyMapLite::setSkyRotation(double skyRotation) 0606 { 0607 if (m_skyRotation != skyRotation) 0608 { 0609 m_skyRotation = skyRotation; 0610 emit skyRotationChanged(skyRotation); 0611 0612 if (skyRotation >= 0 && skyRotation < 90) 0613 { 0614 m_skyMapOrientation = SkyMapOrientation::Top0; 0615 } 0616 else if (skyRotation >= 90 && skyRotation < 180) 0617 { 0618 m_skyMapOrientation = SkyMapOrientation::Right90; 0619 } 0620 else if (skyRotation >= 180 && skyRotation < 270) 0621 { 0622 m_skyMapOrientation = SkyMapOrientation::Bottom180; 0623 } 0624 else if (skyRotation >= 270 && skyRotation < 360) 0625 { 0626 m_skyMapOrientation = SkyMapOrientation::Left270; 0627 } 0628 } 0629 } 0630 0631 void SkyMapLite::setZoomFactor(double factor) 0632 { 0633 Options::setZoomFactor(KSUtils::clamp(factor, MINZOOM, MAXZOOM)); 0634 0635 forceUpdate(); 0636 emit zoomChanged(); 0637 } 0638 0639 void SkyMapLite::forceUpdate() 0640 { 0641 setupProjector(); 0642 0643 // We delay one draw cycle before re-indexing 0644 // we MUST ensure CLines do not get re-indexed while we use DRAW_BUF 0645 // so we do it here. 0646 //m_CLines->reindex( &m_reindexNum ); 0647 // This queues re-indexing for the next draw cycle 0648 //m_reindexNum = KSNumbers( data->updateNum()->julianDay() ); 0649 0650 // This ensures that the JIT updates are synchronized for the entire draw 0651 // cycle so the sky moves as a single sheet. May not be needed. 0652 data->syncUpdateIDs(); 0653 0654 SkyMesh *m_skyMesh = SkyMesh::Instance(3); 0655 if (m_skyMesh) 0656 { 0657 // prepare the aperture 0658 // FIXME_FOV: We may want to rejigger this to allow 0659 // wide-angle views --hdevalence 0660 double radius = m_proj->fov(); 0661 if (radius > 180.0) 0662 radius = 180.0; 0663 0664 if (m_skyMesh->inDraw()) 0665 { 0666 printf("Warning: aborting concurrent SkyMapComposite::draw()\n"); 0667 return; 0668 } 0669 0670 //m_skyMesh->inDraw( true ); 0671 m_skyMesh->aperture(&Focus, radius + 1.0, DRAW_BUF); // divide by 2 for testing 0672 0673 // create the no-precess aperture if needed 0674 if (Options::showEquatorialGrid() || Options::showHorizontalGrid() || Options::showCBounds() || 0675 Options::showEquator()) 0676 { 0677 m_skyMesh->index(&Focus, radius + 1.0, NO_PRECESS_BUF); 0678 } 0679 } 0680 update(); 0681 } 0682 0683 void SkyMapLite::slotUpdateSky(bool now) 0684 { 0685 Q_UNUSED(now); 0686 updateFocus(); 0687 forceUpdate(); 0688 } 0689 0690 void SkyMapLite::updateFocus() 0691 { 0692 if (getSlewing()) 0693 return; 0694 0695 //Tracking on an object 0696 if (Options::isTracking() && focusObject() != nullptr) 0697 { 0698 if (Options::useAltAz()) 0699 { 0700 //Tracking any object in Alt/Az mode requires focus updates 0701 focusObject()->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0702 setFocusAltAz(focusObject()->alt(), focusObject()->az()); 0703 focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat()); 0704 setDestination(*focus()); 0705 } 0706 else 0707 { 0708 //Tracking in equatorial coords 0709 setFocus(focusObject()); 0710 focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0711 setDestination(*focus()); 0712 } 0713 0714 //Tracking on empty sky 0715 } 0716 else if (Options::isTracking() && focusPoint() != nullptr) 0717 { 0718 if (Options::useAltAz()) 0719 { 0720 //Tracking on empty sky in Alt/Az mode 0721 setFocus(focusPoint()); 0722 focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0723 setDestination(*focus()); 0724 } 0725 0726 // Not tracking and not slewing, let sky drift by 0727 // This means that horizontal coordinates are constant. 0728 } 0729 else 0730 { 0731 focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat()); 0732 } 0733 } 0734 0735 void SkyMapLite::setupProjector() 0736 { 0737 //Update View Parameters for projection 0738 ViewParams p; 0739 p.focus = focus(); 0740 p.height = height(); 0741 p.width = width(); 0742 p.useAltAz = Options::useAltAz(); 0743 p.useRefraction = Options::useRefraction(); 0744 p.zoomFactor = Options::zoomFactor(); 0745 p.rotationAngle = Options::skyRotation(); 0746 p.fillGround = Options::showGround(); 0747 0748 //Check if we need a new projector 0749 if (m_proj && Options::projection() == m_proj->type()) 0750 m_proj->setViewParams(p); 0751 else 0752 { 0753 delete m_proj; 0754 switch (Options::projection()) 0755 { 0756 case Projector::Gnomonic: 0757 m_proj = new GnomonicProjector(p); 0758 break; 0759 case Projector::Stereographic: 0760 m_proj = new StereographicProjector(p); 0761 break; 0762 case Projector::Orthographic: 0763 m_proj = new OrthographicProjector(p); 0764 break; 0765 case Projector::AzimuthalEquidistant: 0766 m_proj = new AzimuthalEquidistantProjector(p); 0767 break; 0768 case Projector::Equirectangular: 0769 m_proj = new EquirectangularProjector(p); 0770 break; 0771 case Projector::Lambert: 0772 default: 0773 //TODO: implement other projection classes 0774 m_proj = new LambertProjector(p); 0775 break; 0776 } 0777 } 0778 } 0779 0780 void SkyMapLite::setZoomMouseCursor() 0781 { 0782 mouseMoveCursor = false; // no mousemove cursor 0783 QBitmap cursor = zoomCursorBitmap(2); 0784 QBitmap mask = zoomCursorBitmap(4); 0785 setCursor(QCursor(cursor, mask)); 0786 } 0787 0788 void SkyMapLite::setDefaultMouseCursor() 0789 { 0790 mouseMoveCursor = false; // no mousemove cursor 0791 QBitmap cursor = defaultCursorBitmap(2); 0792 QBitmap mask = defaultCursorBitmap(3); 0793 setCursor(QCursor(cursor, mask)); 0794 } 0795 0796 void SkyMapLite::setMouseMoveCursor() 0797 { 0798 if (mouseButtonDown) 0799 { 0800 setCursor(Qt::SizeAllCursor); // cursor shape defined in qt 0801 mouseMoveCursor = true; 0802 } 0803 } 0804 0805 bool SkyMapLite::isSlewing() const 0806 { 0807 return (getSlewing() || (clockSlewing && data->clock()->isActive())); 0808 } 0809 0810 int SkyMapLite::harvardToIndex(char c) 0811 { 0812 // Convert spectral class to numerical index. 0813 // If spectral class is invalid return index for white star (A class) 0814 0815 switch (c) 0816 { 0817 case 'o': 0818 case 'O': 0819 return 0; 0820 case 'b': 0821 case 'B': 0822 return 1; 0823 case 'a': 0824 case 'A': 0825 return 2; 0826 case 'f': 0827 case 'F': 0828 return 3; 0829 case 'g': 0830 case 'G': 0831 return 4; 0832 case 'k': 0833 case 'K': 0834 return 5; 0835 case 'm': 0836 case 'M': 0837 return 6; 0838 // For unknown spectral class assume A class (white star) 0839 default: 0840 return 2; 0841 } 0842 } 0843 0844 QVector<QVector<QPixmap *>> SkyMapLite::getImageCache() 0845 { 0846 return imageCache; 0847 } 0848 0849 QSGTexture *SkyMapLite::textToTexture(QString text, QColor color, bool zoomFont) 0850 { 0851 if (isInitialized) 0852 { 0853 QFont f; 0854 0855 if (zoomFont) 0856 { 0857 f = SkyLabeler::Instance()->drawFont(); 0858 } 0859 else 0860 { 0861 f = SkyLabeler::Instance()->stdFont(); 0862 } 0863 0864 qreal ratio = window()->effectiveDevicePixelRatio(); 0865 0866 QFontMetrics fm(f); 0867 0868 int width = fm.width(text); 0869 int height = fm.height(); 0870 // double rotation = 0; 0871 0872 //// switch(m_skyMapOrientation) { 0873 //// case(SkyMapOrientation::Top0): 0874 //// width = fm.width(text); 0875 //// height = fm.height(); 0876 //// rotation = 0; 0877 //// break; 0878 //// case(SkyMapOrientation::Right90): 0879 //// width = fm.height(); 0880 //// height = fm.width(text); 0881 //// rotation = 90; 0882 //// case(SkyMapOrientation::Bottom180): 0883 ////// width = ; 0884 ////// height = ; 0885 //// rotation = 180; 0886 //// case(SkyMapOrientation::Left270): 0887 ////// width = ; 0888 ////// height; 0889 //// rotation = 270; 0890 //// } 0891 0892 f.setPointSizeF(f.pointSizeF() * ratio); 0893 0894 QImage label((width)*ratio, (height)*ratio, QImage::Format_ARGB32_Premultiplied); 0895 0896 label.fill(Qt::transparent); 0897 0898 m_painter.begin(&label); 0899 0900 m_painter.setFont(f); 0901 0902 m_painter.setPen(color); 0903 0904 // m_painter.drawRect(0,0, label.width(), label.height()); 0905 // m_painter.rotate(getSkyRotation()); 0906 m_painter.drawText(0, (height - fm.descent()) * ratio, text); 0907 0908 m_painter.end(); 0909 0910 QSGTexture *texture = window()->createTextureFromImage(label, QQuickWindow::TextureCanUseAtlas); 0911 return texture; 0912 } 0913 else 0914 { 0915 return nullptr; 0916 } 0917 } 0918 0919 void SkyMapLite::addFOVSymbol(const QString &FOVName, bool initialState) 0920 { 0921 m_FOVSymbols.append(FOVName); 0922 //Emit signal whenever new value was added 0923 emit symbolsFOVChanged(m_FOVSymbols); 0924 0925 m_FOVSymVisible.append(initialState); 0926 } 0927 0928 bool SkyMapLite::isFOVVisible(int index) 0929 { 0930 return m_FOVSymVisible.value(index); 0931 } 0932 0933 void SkyMapLite::setFOVVisible(int index, bool visible) 0934 { 0935 if (index >= 0 && index < m_FOVSymVisible.size()) 0936 { 0937 m_FOVSymVisible[index] = visible; 0938 forceUpdate(); 0939 } 0940 } 0941 0942 void SkyMapLite::setSlewing(bool newSlewing) 0943 { 0944 if (m_slewing != newSlewing) 0945 { 0946 m_slewing = newSlewing; 0947 emit slewingChanged(newSlewing); 0948 } 0949 } 0950 0951 void SkyMapLite::setCenterLocked(bool centerLocked) 0952 { 0953 m_centerLocked = centerLocked; 0954 emit centerLockedChanged(centerLocked); 0955 } 0956 0957 void SkyMapLite::setAutomaticMode(bool automaticMode) 0958 { 0959 #if defined(Q_OS_ANDROID) 0960 if (m_automaticMode != automaticMode) 0961 { 0962 m_automaticMode = automaticMode; 0963 if (automaticMode) 0964 { 0965 m_deviceOrientation->startSensors(); 0966 automaticModeTimer.start(); 0967 } 0968 else 0969 { 0970 automaticModeTimer.stop(); 0971 m_deviceOrientation->stopSensors(); 0972 } 0973 } 0974 #else 0975 Q_UNUSED(automaticMode); 0976 #endif 0977 } 0978 0979 void SkyMapLite::updateAutomaticMode() 0980 { 0981 #if defined(Q_OS_ANDROID) 0982 m_deviceOrientation->getOrientation(); 0983 if (Options::useRefraction() && Options::useAltAz()) 0984 { 0985 setFocusAltAz(SkyPoint::unrefract(dms(m_deviceOrientation->getAltitude())), dms(m_deviceOrientation->getAzimuth())); 0986 } 0987 else 0988 { 0989 setFocusAltAz(dms(m_deviceOrientation->getAltitude()), dms(m_deviceOrientation->getAzimuth())); 0990 } 0991 0992 setSkyRotation(-1 * m_deviceOrientation->getRoll()); 0993 #endif 0994 } 0995 0996 void SkyMapLite::initStarImages() 0997 { 0998 if (isInitialized) 0999 { 1000 //Delete all existing pixmaps 1001 if (imageCache.length() != 0) 1002 { 1003 foreach (QVector<QPixmap *> vec, imageCache) 1004 { 1005 qDeleteAll(vec.begin(), vec.end()); 1006 } 1007 clearTextures = true; 1008 } 1009 1010 imageCache = QVector<QVector<QPixmap *>>(nSPclasses); 1011 1012 QMap<char, QColor> ColorMap; 1013 const int starColorIntensity = Options::starColorIntensity(); 1014 1015 //On high-dpi screens star will look pixelized if don't multiply scaling factor by this ratio 1016 //Check PointNode::setNode() to see how it works 1017 qreal ratio = window()->effectiveDevicePixelRatio(); 1018 1019 switch (Options::starColorMode()) 1020 { 1021 case 1: // Red stars. 1022 ColorMap.insert('O', QColor::fromRgb(255, 0, 0)); 1023 ColorMap.insert('B', QColor::fromRgb(255, 0, 0)); 1024 ColorMap.insert('A', QColor::fromRgb(255, 0, 0)); 1025 ColorMap.insert('F', QColor::fromRgb(255, 0, 0)); 1026 ColorMap.insert('G', QColor::fromRgb(255, 0, 0)); 1027 ColorMap.insert('K', QColor::fromRgb(255, 0, 0)); 1028 ColorMap.insert('M', QColor::fromRgb(255, 0, 0)); 1029 break; 1030 case 2: // Black stars. 1031 ColorMap.insert('O', QColor::fromRgb(0, 0, 0)); 1032 ColorMap.insert('B', QColor::fromRgb(0, 0, 0)); 1033 ColorMap.insert('A', QColor::fromRgb(0, 0, 0)); 1034 ColorMap.insert('F', QColor::fromRgb(0, 0, 0)); 1035 ColorMap.insert('G', QColor::fromRgb(0, 0, 0)); 1036 ColorMap.insert('K', QColor::fromRgb(0, 0, 0)); 1037 ColorMap.insert('M', QColor::fromRgb(0, 0, 0)); 1038 break; 1039 case 3: // White stars 1040 ColorMap.insert('O', QColor::fromRgb(255, 255, 255)); 1041 ColorMap.insert('B', QColor::fromRgb(255, 255, 255)); 1042 ColorMap.insert('A', QColor::fromRgb(255, 255, 255)); 1043 ColorMap.insert('F', QColor::fromRgb(255, 255, 255)); 1044 ColorMap.insert('G', QColor::fromRgb(255, 255, 255)); 1045 ColorMap.insert('K', QColor::fromRgb(255, 255, 255)); 1046 ColorMap.insert('M', QColor::fromRgb(255, 255, 255)); 1047 case 0: // Real color 1048 default: // And use real color for everything else 1049 ColorMap.insert('O', QColor::fromRgb(0, 0, 255)); 1050 ColorMap.insert('B', QColor::fromRgb(0, 200, 255)); 1051 ColorMap.insert('A', QColor::fromRgb(0, 255, 255)); 1052 ColorMap.insert('F', QColor::fromRgb(200, 255, 100)); 1053 ColorMap.insert('G', QColor::fromRgb(255, 255, 0)); 1054 ColorMap.insert('K', QColor::fromRgb(255, 100, 0)); 1055 ColorMap.insert('M', QColor::fromRgb(255, 0, 0)); 1056 } 1057 1058 for (char color : ColorMap.keys()) 1059 { 1060 //Add new spectral class 1061 1062 QPixmap BigImage(15, 15); 1063 BigImage.fill(Qt::transparent); 1064 1065 QPainter p; 1066 p.begin(&BigImage); 1067 1068 if (Options::starColorMode() == 0) 1069 { 1070 qreal h, s, v, a; 1071 p.setRenderHint(QPainter::Antialiasing, false); 1072 QColor starColor = ColorMap[color]; 1073 starColor.getHsvF(&h, &s, &v, &a); 1074 for (int i = 0; i < 8; i++) 1075 { 1076 for (int j = 0; j < 8; j++) 1077 { 1078 qreal x = i - 7; 1079 qreal y = j - 7; 1080 qreal dist = sqrt(x * x + y * y) / 7.0; 1081 starColor.setHsvF(h, qMin(qreal(1), dist < (10 - starColorIntensity) / 10.0 ? 0 : dist), v, 1082 qMax(qreal(0), dist < (10 - starColorIntensity) / 20.0 ? 1 : 1 - dist)); 1083 p.setPen(starColor); 1084 p.drawPoint(i, j); 1085 p.drawPoint((14 - i), j); 1086 p.drawPoint(i, (14 - j)); 1087 p.drawPoint((14 - i), (14 - j)); 1088 } 1089 } 1090 } 1091 else 1092 { 1093 p.setRenderHint(QPainter::Antialiasing, true); 1094 p.setPen(QPen(ColorMap[color], 2.0)); 1095 p.setBrush(p.pen().color()); 1096 p.drawEllipse(QRectF(2, 2, 10, 10)); 1097 } 1098 p.end(); 1099 //[nSPclasses][nStarSizes]; 1100 // Cache array slice 1101 1102 QVector<QPixmap *> *pmap = &imageCache[harvardToIndex(color)]; 1103 pmap->append(new QPixmap(BigImage)); 1104 for (int size = 1; size < nStarSizes; size++) 1105 { 1106 pmap->append(new QPixmap( 1107 BigImage.scaled(size * ratio, size * ratio, Qt::KeepAspectRatio, Qt::SmoothTransformation))); 1108 } 1109 } 1110 //} 1111 starColorMode = Options::starColorMode(); 1112 } 1113 }