File indexing completed on 2025-04-27 09:56:22
0001 /* 0002 SPDX-FileCopyrightText: 2016 Artem Fedoskin <afedoskin3@gmail.com> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 #include "skymaplite.h" 0006 #include "kstarsdata.h" 0007 #include "kstarslite.h" 0008 0009 #include <QtMath> 0010 0011 #include "kstarslite/skypointlite.h" 0012 #include "kstarslite/skyobjectlite.h" 0013 0014 #include "kstarslite/skyitems/planetsitem.h" 0015 #include "Options.h" 0016 #include "projections/projector.h" 0017 #include "skymapcomposite.h" 0018 #include "ksutils.h" 0019 //#include <QTapSensor> 0020 0021 void SkyMapLite::mousePressEvent(QMouseEvent *e) 0022 { 0023 if ((e->modifiers() & Qt::ControlModifier) && (e->button() == Qt::LeftButton)) 0024 { 0025 ZoomRect.moveCenter(e->pos()); 0026 setZoomMouseCursor(); 0027 update(); //refresh without redrawing skymap 0028 return; 0029 } 0030 // if button is down and cursor is not moved set the move cursor after 500 ms 0031 QTimer::singleShot(500, this, SLOT(setMouseMoveCursor())); 0032 0033 // break if point is unusable 0034 if (projector()->unusablePoint(e->pos())) 0035 return; 0036 0037 if (!midMouseButtonDown && e->button() == Qt::MidButton) 0038 { 0039 y0 = 0.5 * height() - e->y(); //record y pixel coordinate for middle-button zooming 0040 midMouseButtonDown = true; 0041 } 0042 if (!mouseButtonDown) 0043 { 0044 if (e->button() == Qt::LeftButton) 0045 { 0046 mouseButtonDown = true; 0047 } 0048 0049 //determine RA, Dec of mouse pointer 0050 m_MousePoint = projector()->fromScreen(e->pos(), data->lst(), data->geo()->lat()); 0051 setClickedPoint(&m_MousePoint); 0052 0053 //Find object nearest to clickedPoint() 0054 double maxrad = 1000.0 / Options::zoomFactor(); 0055 SkyObject *obj = data->skyComposite()->objectNearest(clickedPoint(), maxrad); 0056 setClickedObject(obj); 0057 if (obj) 0058 setClickedPoint(obj); 0059 0060 switch (e->button()) 0061 { 0062 case Qt::LeftButton: 0063 { 0064 /*QString name; 0065 if( clickedObject() ) 0066 //name = clickedObject()->translatedLongName(); 0067 else 0068 //name = i18n( "Empty sky" ); 0069 //kstars->statusBar()->changeItem(name, 0 ); 0070 //kstars->statusBar()->showMessage(name, 0 ); 0071 0072 emit positionClicked(&m_MousePoint);*/ 0073 } 0074 0075 break; 0076 case Qt::RightButton: 0077 /*if( rulerMode ) { 0078 // Compute angular distance. 0079 slotEndRulerMode(); 0080 } else {*/ 0081 // Show popup menu 0082 if (clickedObject()) 0083 { 0084 emit objectLiteChanged(); 0085 } 0086 else 0087 { 0088 emit pointLiteChanged(); 0089 /* pmenu->createEmptyMenu( clickedPoint() ); 0090 pmenu->popup( QCursor::pos() );*/ 0091 } 0092 //} 0093 break; 0094 default: 0095 ; 0096 } 0097 } 0098 } 0099 0100 void SkyMapLite::mouseReleaseEvent(QMouseEvent *) 0101 { 0102 /*if ( ZoomRect.isValid() ) { 0103 stopTracking(); 0104 SkyPoint newcenter = projector()->fromScreen( ZoomRect.center(), data->lst(), data->geo()->lat() ); 0105 setFocus( &newcenter ); 0106 setDestination( newcenter ); 0107 0108 //Zoom in on center of Zoom Circle, by a factor equal to the ratio 0109 //of the sky pixmap's width to the Zoom Circle's diameter 0110 float factor = float(width()) / float(ZoomRect.width()); 0111 setZoomFactor( Options::zoomFactor() * factor ); 0112 }*/ 0113 setDefaultMouseCursor(); 0114 ZoomRect = QRect(); //invalidate ZoomRect 0115 0116 /*if(m_previewLegend) { 0117 slotCancelLegendPreviewMode(); 0118 }*/ 0119 0120 //false if double-clicked, because it's unset there. 0121 if (mouseButtonDown) 0122 { 0123 mouseButtonDown = false; 0124 if (getSlewing()) 0125 { 0126 setSlewing(false); 0127 if (Options::useAltAz()) 0128 setDestinationAltAz(focus()->alt(), focus()->az(), false); 0129 else 0130 setDestination(*focus()); 0131 } 0132 forceUpdate(); // is needed because after moving the sky not all stars are shown 0133 } 0134 // if middle button was pressed unset here 0135 midMouseButtonDown = false; 0136 } 0137 0138 void SkyMapLite::mouseDoubleClickEvent(QMouseEvent *e) 0139 { 0140 if (e->button() == Qt::LeftButton && !projector()->unusablePoint(e->pos())) 0141 { 0142 mouseButtonDown = false; 0143 if (e->x() != width() / 2 || e->y() != height() / 2) 0144 slotCenter(); 0145 } 0146 } 0147 0148 void SkyMapLite::mouseMoveEvent(QMouseEvent *e) 0149 { 0150 if (Options::useHoverLabel()) 0151 { 0152 //Start a single-shot timer to monitor whether we are currently hovering. 0153 //The idea is that whenever a moveEvent occurs, the timer is reset. It 0154 //will only timeout if there are no move events for HOVER_INTERVAL ms 0155 m_HoverTimer.start(HOVER_INTERVAL); 0156 //DELETE? QToolTip::hideText(); 0157 } 0158 0159 //Are we defining a ZoomRect? 0160 /*if ( ZoomRect.center().x() > 0 && ZoomRect.center().y() > 0 ) { 0161 //cancel operation if the user let go of CTRL 0162 if ( !( e->modifiers() & Qt::ControlModifier ) ) { 0163 ZoomRect = QRect(); //invalidate ZoomRect 0164 update(); 0165 } else { 0166 //Resize the rectangle so that it passes through the cursor position 0167 QPoint pcenter = ZoomRect.center(); 0168 int dx = abs(e->x() - pcenter.x()); 0169 int dy = abs(e->y() - pcenter.y()); 0170 if ( dx == 0 || float(dy)/float(dx) > float(height())/float(width()) ) { 0171 //Size rect by height 0172 ZoomRect.setHeight( 2*dy ); 0173 ZoomRect.setWidth( 2*dy*width()/height() ); 0174 } else { 0175 //Size rect by height 0176 ZoomRect.setWidth( 2*dx ); 0177 ZoomRect.setHeight( 2*dx*height()/width() ); 0178 } 0179 ZoomRect.moveCenter( pcenter ); //reset center 0180 0181 update(); 0182 return; 0183 } 0184 } 0185 */ 0186 if (projector()->unusablePoint(e->pos())) 0187 return; // break if point is unusable 0188 0189 //determine RA, Dec of mouse pointer 0190 m_MousePoint = projector()->fromScreen(e->pos(), data->lst(), data->geo()->lat()); 0191 double dyPix = 0.5 * height() - e->y(); 0192 if (midMouseButtonDown) //zoom according to y-offset 0193 { 0194 float yoff = dyPix - y0; 0195 if (yoff > 10) 0196 { 0197 y0 = dyPix; 0198 slotZoomIn(); 0199 } 0200 if (yoff < -10) 0201 { 0202 y0 = dyPix; 0203 slotZoomOut(); 0204 } 0205 } 0206 if (mouseButtonDown) 0207 { 0208 // set the mouseMoveCursor and set slewing to true, if they are not set yet 0209 if (!mouseMoveCursor) 0210 setMouseMoveCursor(); 0211 if (!getSlewing()) 0212 { 0213 setSlewing(true); 0214 stopTracking(); //toggle tracking off 0215 } 0216 0217 //Update focus such that the sky coords at mouse cursor remain approximately constant 0218 if (Options::useAltAz()) 0219 { 0220 m_MousePoint.EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0221 clickedPoint()->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0222 dms dAz = m_MousePoint.az() - clickedPoint()->az(); 0223 dms dAlt = m_MousePoint.altRefracted() - clickedPoint()->altRefracted(); 0224 focus()->setAz(focus()->az().Degrees() - dAz.Degrees()); //move focus in opposite direction 0225 focus()->setAz(focus()->az().reduce()); 0226 focus()->setAltRefracted(KSUtils::clamp(focus()->altRefracted().Degrees() - dAlt.Degrees(), -90.0, 90.0)); 0227 focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat()); 0228 } 0229 else 0230 { 0231 dms dRA = m_MousePoint.ra() - clickedPoint()->ra(); 0232 dms dDec = m_MousePoint.dec() - clickedPoint()->dec(); 0233 focus()->setRA(focus()->ra().Hours() - dRA.Hours()); //move focus in opposite direction 0234 focus()->setRA(focus()->ra().reduce()); 0235 focus()->setDec(KSUtils::clamp(focus()->dec().Degrees() - dDec.Degrees(), -90.0, 90.0)); 0236 focus()->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0237 } 0238 //showFocusCoords(); 0239 0240 //redetermine RA, Dec of mouse pointer, using new focus 0241 m_MousePoint = projector()->fromScreen(e->pos(), data->lst(), data->geo()->lat()); 0242 setClickedPoint(&m_MousePoint); 0243 0244 forceUpdate(); // must be new computed 0245 } 0246 else //mouse button not down 0247 { 0248 if (Options::useAltAz()) 0249 m_MousePoint.EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0250 emit mousePointChanged(&m_MousePoint); 0251 } 0252 } 0253 0254 void SkyMapLite::wheelEvent(QWheelEvent *e) 0255 { 0256 if (e->delta() > 0) 0257 { 0258 zoomInOrMagStep(e->modifiers()); 0259 //setRotation(rotation() + 0.1); TEST 0260 } 0261 else if (e->delta() < 0) 0262 { 0263 zoomOutOrMagStep(e->modifiers()); 0264 //setRotation(rotation() - 0.1); TEST 0265 } 0266 } 0267 0268 void SkyMapLite::touchEvent(QTouchEvent *e) 0269 { 0270 QList<QTouchEvent::TouchPoint> points = e->touchPoints(); 0271 0272 bool autoMode = false; //Always false for devices that doesn't support automatic mode 0273 #if defined(Q_OS_ANDROID) 0274 autoMode = getAutomaticMode(); 0275 #endif 0276 0277 if (points.length() == 2) 0278 { 0279 //Set tapBegan to false because user doesn't tap but either pans or pinches to zoom 0280 tapBegan = false; 0281 if (projector()->unusablePoint(points[0].pos()) || projector()->unusablePoint(points[1].pos())) 0282 return; 0283 0284 //Pinch to zoom 0285 double x_old_diff = qFabs(points[1].lastPos().x() - points[0].lastPos().x()); 0286 double y_old_diff = qFabs(points[1].lastPos().y() - points[0].lastPos().y()); 0287 0288 //Manhattan distance of old points 0289 double md_old = x_old_diff + y_old_diff; 0290 0291 double x_diff = qFabs(points[1].pos().x() - points[0].pos().x()); 0292 double y_diff = qFabs(points[1].pos().y() - points[0].pos().y()); 0293 0294 //Manhattan distance of new points 0295 double md_new = x_diff + y_diff; 0296 0297 int zoomThreshold = 5; 0298 0299 if (md_old - md_new < -zoomThreshold) 0300 zoomInOrMagStep(Qt::MetaModifier); 0301 else if (md_old - md_new > zoomThreshold) 0302 zoomOutOrMagStep(Qt::MetaModifier); 0303 0304 double x_min = points[1].pos().x() < points[0].pos().x() ? points[1].pos().x() : points[0].pos().x(); 0305 double y_min = points[1].pos().y() < points[0].pos().y() ? points[1].pos().y() : points[0].pos().y(); 0306 0307 //Center point on the line between 2 touch points used for moveEvent 0308 QPointF pinchCenter(x_min + x_diff / 2, y_min + y_diff / 2); 0309 0310 //Pinch(turn) to rotate 0311 QPointF old_vec = points[1].lastPos() - points[0].lastPos(); 0312 QPointF new_vec = points[1].pos() - points[0].pos(); 0313 0314 double old_vec_len = sqrt((old_vec.x() * old_vec.x()) + (old_vec.y() * old_vec.y())); 0315 double new_vec_len = sqrt((new_vec.x() * new_vec.x()) + (new_vec.y() * new_vec.y())); 0316 0317 //Normalize vectors 0318 old_vec = QPointF(old_vec.x() / old_vec_len, old_vec.y() / old_vec_len); 0319 new_vec = QPointF(new_vec.x() / new_vec_len, new_vec.y() / new_vec_len); 0320 0321 double old_atan = qAtan2(old_vec.y(), old_vec.x()); 0322 double new_atan = qAtan2(new_vec.y(), new_vec.x()); 0323 0324 /*Workaround to solve the strange bug when sometimes signs of qAtan2 results 0325 for 2 vectors are different*/ 0326 if ((new_atan > 0 && old_atan < 0) || (new_atan < 0 && old_atan > 0)) 0327 { 0328 old_atan *= -1; 0329 } 0330 0331 //Get the angle between two vectors 0332 /*double delta = new_atan - old_atan; 0333 0334 //Rotation is under construction 0335 if(rotation() > 360) { 0336 setRotation(0); 0337 } else if(rotation() < 0) { 0338 setRotation(360); 0339 } 0340 0341 //Scale the angle to speed up the rotation 0342 delta *= 100; 0343 setRotation(rotation() + delta); 0344 //update(); //Apply rotation*/ 0345 0346 //Allow movement of SkyMapLite while rotating or zooming 0347 if (!getCenterLocked() && !autoMode) 0348 { 0349 QMouseEvent *event = new QMouseEvent(QEvent::MouseButtonPress, pinchCenter, Qt::LeftButton, Qt::LeftButton, 0350 Qt::ControlModifier); 0351 if (!pinch) 0352 { 0353 m_MousePoint = projector()->fromScreen(pinchCenter, data->lst(), data->geo()->lat()); 0354 setClickedPoint(&m_MousePoint); 0355 mouseButtonDown = true; 0356 pinch = true; 0357 } 0358 mouseMoveEvent(event); 0359 delete event; 0360 } 0361 0362 if (e->touchPointStates() & Qt::TouchPointReleased) 0363 { 0364 setSlewing(false); 0365 if (pinch) 0366 { 0367 pinch = false; 0368 mouseButtonDown = false; 0369 } 0370 } 0371 } 0372 else if (points.length() == 1 && !pinch && !autoMode) 0373 { 0374 QPointF point = points[0].screenPos(); 0375 //Set clicked point (needed for pan) 0376 if (e->type() == QEvent::TouchBegin) 0377 { 0378 m_MousePoint = projector()->fromScreen(point, data->lst(), data->geo()->lat()); 0379 setClickedPoint(&m_MousePoint); 0380 mouseButtonDown = true; 0381 } 0382 else if (e->type() == QEvent::TouchEnd) 0383 { 0384 mouseButtonDown = false; 0385 if (getSlewing()) 0386 { 0387 setSlewing(false); 0388 if (Options::useAltAz()) 0389 setDestinationAltAz(focus()->alt(), focus()->az(), false); 0390 else 0391 setDestination(*focus()); 0392 } 0393 } 0394 0395 if (!projector()->unusablePoint(points[0].screenPos())) 0396 { 0397 if (!tapBegan && (e->touchPointStates() & Qt::TouchPointPressed)) 0398 { 0399 //We set tapBegan to true whenever user tapped on the screen 0400 tapBegan = true; 0401 m_tapBeganTimer.start(100); 0402 } 0403 else if ((e->touchPointStates() & Qt::TouchPointMoved) || getSlewing()) 0404 { 0405 //Set tapBegan to false because user doesn't tap but either pans or pinches to zoom 0406 if (m_tapBeganTimer.remainingTime() > 0) 0407 { 0408 return; 0409 } 0410 else 0411 { 0412 m_tapBeganTimer.stop(); 0413 } 0414 tapBegan = false; 0415 0416 QMouseEvent *event = new QMouseEvent(QEvent::MouseButtonPress, point, Qt::LeftButton, Qt::LeftButton, 0417 Qt::ControlModifier); 0418 mouseMoveEvent(event); 0419 delete event; 0420 0421 //If user didn't pan and pinch to zoom tapBegan will be true 0422 } 0423 else if ((e->touchPointStates() & Qt::TouchPointReleased) && tapBegan) 0424 { 0425 if (getSlewing()) 0426 setSlewing(false); 0427 tapBegan = false; 0428 //Show tap animation 0429 emit posClicked(point); 0430 //determine RA, Dec of touch 0431 m_MousePoint = projector()->fromScreen(point, data->lst(), data->geo()->lat()); 0432 setClickedPoint(&m_MousePoint); 0433 0434 //Find object nearest to clickedPoint() 0435 double maxrad = 0436 1000.0 / 0437 Options:: 0438 zoomFactor(); /* On high zoom-level it is very hard to select the object using touch screen. 0439 That's why radius remains constant*/ 0440 maxrad = qMax(maxrad, 2.5); 0441 SkyObject *obj = data->skyComposite()->objectNearest(clickedPoint(), maxrad); 0442 setClickedObject(obj); 0443 if (obj) 0444 setClickedPoint(obj); 0445 0446 if (clickedObject()) 0447 { 0448 emit objectLiteChanged(); 0449 } 0450 else 0451 { 0452 emit pointLiteChanged(); 0453 } 0454 } 0455 } 0456 } 0457 } 0458 0459 double SkyMapLite::zoomFactor(const int modifier) 0460 { 0461 double factor = (modifier & Qt::ControlModifier) ? DZOOM : 2.0; 0462 if (modifier & Qt::ShiftModifier) 0463 factor = sqrt(factor); 0464 return factor; 0465 } 0466 0467 void SkyMapLite::zoomInOrMagStep(const int modifier) 0468 { 0469 if (modifier & Qt::AltModifier) 0470 incMagLimit(modifier); 0471 else if (modifier & Qt::MetaModifier) //Used in pinch-to-zoom gesture 0472 { 0473 setZoomFactor(Options::zoomFactor() + Options::zoomFactor() * 0.05); 0474 } 0475 else 0476 setZoomFactor(Options::zoomFactor() * zoomFactor(modifier)); 0477 } 0478 0479 void SkyMapLite::zoomOutOrMagStep(const int modifier) 0480 { 0481 if (modifier & Qt::AltModifier) 0482 decMagLimit(modifier); 0483 else if (modifier & Qt::MetaModifier) //Used in pinch-to-zoom gesture 0484 { 0485 setZoomFactor(Options::zoomFactor() - Options::zoomFactor() * 0.05); 0486 } 0487 else 0488 setZoomFactor(Options::zoomFactor() / zoomFactor(modifier)); 0489 } 0490 0491 double SkyMapLite::magFactor(const int modifier) 0492 { 0493 double factor = (modifier & Qt::ControlModifier) ? 0.1 : 0.5; 0494 if (modifier & Qt::ShiftModifier) 0495 factor *= 2.0; 0496 return factor; 0497 } 0498 0499 void SkyMapLite::setMagLim(double magLim) 0500 { 0501 if (m_magLim != magLim) 0502 { 0503 m_magLim = magLim; 0504 if (m_magLim > 5.75954) 0505 m_magLim = 5.75954; 0506 if (m_magLim < 1.18778) 0507 m_magLim = 1.18778; 0508 emit magLimChanged(m_magLim); 0509 0510 Options::setStarDensity(pow(10, (m_magLim - 0.35) / 2.222)); 0511 //printf("maglim set to %3.1f\n", m_magLim); 0512 forceUpdate(); 0513 } 0514 } 0515 0516 void SkyMapLite::stopTracking() 0517 { 0518 KStarsLite *kstars = KStarsLite::Instance(); 0519 emit positionChanged(); 0520 if (kstars && Options::isTracking()) 0521 kstars->slotTrack(); 0522 } 0523 0524 uint SkyMapLite::projType() const 0525 { 0526 return m_proj->type(); 0527 } 0528 0529 void SkyMapLite::incMagLimit(const int modifier) 0530 { 0531 m_magLim = 2.222 * log10(static_cast<double>(Options::starDensity())) + 0.35; 0532 m_magLim += magFactor(modifier); 0533 if (m_magLim > 5.75954) 0534 m_magLim = 5.75954; 0535 emit magLimChanged(m_magLim); 0536 Options::setStarDensity(pow(10, (m_magLim - 0.35) / 2.222)); 0537 //printf("maglim set to %3.1f\n", m_magLim); 0538 forceUpdate(); 0539 } 0540 0541 void SkyMapLite::decMagLimit(const int modifier) 0542 { 0543 m_magLim = 2.222 * log10(static_cast<double>(Options::starDensity())) + 0.35; 0544 m_magLim -= magFactor(modifier); 0545 if (m_magLim < 1.18778) 0546 m_magLim = 1.18778; 0547 emit magLimChanged(m_magLim); 0548 Options::setStarDensity(pow(10, (m_magLim - 0.35) / 2.222)); 0549 //printf("maglim set to %3.1f\n", m_magLim); 0550 forceUpdate(); 0551 }