File indexing completed on 2024-04-21 03:44:44
0001 /* 0002 SPDX-FileCopyrightText: 2001 Jason Harris <kstars@30doradus.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "starobject.h" 0008 0009 #include "deepstardata.h" 0010 #include "ksnumbers.h" 0011 #ifndef KSTARS_LITE 0012 #include "kspopupmenu.h" 0013 #endif 0014 #include "kstarsdata.h" 0015 #include "ksutils.h" 0016 #include "Options.h" 0017 #include "skymap.h" 0018 #include "stardata.h" 0019 0020 #include <typeinfo> 0021 0022 #ifdef PROFILE_UPDATECOORDS 0023 double StarObject::updateCoordsCpuTime = 0.; 0024 unsigned int StarObject::starsUpdated = 0; 0025 #include <cstdlib> 0026 #include <ctime> 0027 #endif 0028 0029 // DEBUG EDIT. Uncomment for testing Proper Motion 0030 //#include "skycomponents/skymesh.h" 0031 // END DEBUG 0032 0033 #include "skycomponents/skylabeler.h" 0034 0035 // DEBUG EDIT. Uncomment for testing Proper Motion 0036 // You will also need to uncomment all related blocks 0037 // from this file, starobject.h and also the trixel-boundaries 0038 // block from lines 253 - 257 of skymapcomposite.cpp 0039 //QVector<SkyPoint *> StarObject::Trail; 0040 // END DEBUG 0041 0042 #include <KLocalizedString> 0043 0044 //----- Static Methods ----- 0045 // 0046 double StarObject::reindexInterval(double pm) 0047 { 0048 if (pm < 1.0e-6) 0049 return 1.0e6; 0050 0051 // arcminutes * sec/min * milliarcsec/sec centuries/year 0052 // / [milliarcsec/year] = centuries 0053 0054 return 25.0 * 60.0 * 10.0 / pm; 0055 } 0056 0057 StarObject::StarObject(dms r, dms d, float m, const QString &n, const QString &n2, const QString &sptype, double pmra, 0058 double pmdec, double par, bool mult, bool var, int hd) 0059 : SkyObject(SkyObject::STAR, r, d, m, n, n2, QString()), PM_RA(pmra), PM_Dec(pmdec), Parallax(par), 0060 Multiplicity(mult), Variability(var), HD(hd) 0061 { 0062 QByteArray spt = sptype.toLatin1(); 0063 SpType[0] = spt[0]; 0064 SpType[1] = spt[1]; 0065 0066 QString lname; 0067 if (hasName()) 0068 { 0069 lname = n; 0070 if (hasName2()) 0071 lname += " (" + gname() + ')'; 0072 } 0073 else if (hasName2()) 0074 { 0075 lname = gname(); 0076 //If genetive name exists, but no primary name, set primary name = genetive name. 0077 setName(gname()); 0078 } 0079 else if (HD > 0) 0080 { 0081 lname = QLatin1String("HD ") + QString::number(HD); 0082 } 0083 setLongName(lname); 0084 updateID = updateNumID = 0; 0085 } 0086 0087 StarObject::StarObject(double r, double d, float m, const QString &n, const QString &n2, const QString &sptype, 0088 double pmra, double pmdec, double par, bool mult, bool var, int hd) 0089 : SkyObject(SkyObject::STAR, r, d, m, n, n2, QString()), PM_RA(pmra), PM_Dec(pmdec), Parallax(par), 0090 Multiplicity(mult), Variability(var), HD(hd) 0091 { 0092 QByteArray spt = sptype.toLatin1(); 0093 SpType[0] = spt[0]; 0094 SpType[1] = spt[1]; 0095 0096 QString lname; 0097 if (hasName()) 0098 { 0099 lname = n; 0100 if (hasName2()) 0101 lname += " (" + gname() + ')'; 0102 } 0103 else if (hasName2()) 0104 { 0105 lname = gname(); 0106 //If genetive name exists, but no primary name, set primary name = genetive name. 0107 setName(gname()); 0108 } 0109 else if (HD > 0) 0110 { 0111 lname = QLatin1String("HD ") + QString::number(HD); 0112 } 0113 setLongName(lname); 0114 updateID = updateNumID = 0; 0115 } 0116 0117 StarObject::StarObject(const StarObject &o) 0118 : SkyObject(o), PM_RA(o.PM_RA), PM_Dec(o.PM_Dec), Parallax(o.Parallax), Multiplicity(o.Multiplicity), 0119 Variability(o.Variability), HD(o.HD) 0120 { 0121 SpType[0] = o.SpType[0]; 0122 SpType[1] = o.SpType[1]; 0123 updateID = updateNumID = 0; 0124 } 0125 0126 StarObject *StarObject::clone() const 0127 { 0128 Q_ASSERT(typeid(this) == 0129 typeid(static_cast<const StarObject *>(this))); // Ensure we are not slicing a derived class 0130 return new StarObject(*this); 0131 } 0132 0133 void StarObject::init(const StarData *stardata) 0134 { 0135 double ra, dec; 0136 ra = stardata->RA / 1000000.0; 0137 dec = stardata->Dec / 100000.0; 0138 setType(SkyObject::STAR); 0139 setMag(stardata->mag / 100.0); 0140 setRA0(ra); 0141 setDec0(dec); 0142 setRA(ra0()); 0143 setDec(dec0()); 0144 SpType[0] = stardata->spec_type[0]; 0145 SpType[1] = stardata->spec_type[1]; 0146 PM_RA = stardata->dRA / 10.0; 0147 PM_Dec = stardata->dDec / 10.0; 0148 Parallax = stardata->parallax / 10.0; 0149 Multiplicity = stardata->flags & 0x02; 0150 Variability = stardata->flags & 0x04; 0151 updateID = updateNumID = 0; 0152 HD = stardata->HD; 0153 if (HD > 0) 0154 setNames(QString(QLatin1String("HD ") + QString::number(HD)), QString()); 0155 B = V = 99.9; 0156 0157 // DEBUG Edit. For testing proper motion. Uncomment all related blocks to test. 0158 // WARNING: You can debug only ONE STAR AT A TIME, because 0159 // the StarObject::Trail is static. It has to be 0160 // static, because otherwise, we can run into segfaults 0161 // due to the memcpy() that we do to create stars 0162 /* 0163 testStar = false; 0164 if( stardata->HD == 103095 && Trail.size() == 0 ) { 0165 // Populate Trail with various positions 0166 qDebug() << Q_FUNC_INFO << "TEST STAR FOUND!"; 0167 testStar = true; 0168 KSNumbers num( J2000 ); // Some estimate, doesn't matter. 0169 long double jy; 0170 for( jy = -10000.0; jy <= 10000.0; jy += 500.0 ) { 0171 num.updateValues( J2000 + jy * 365.238 ); 0172 double ra, dec; 0173 getIndexCoords( &num, &ra, &dec ); 0174 Trail.append( new SkyPoint( ra / 15.0, dec ) ); 0175 } 0176 qDebug() << Q_FUNC_INFO << "Populated the star's trail with " << Trail.size() << " entries."; 0177 } 0178 */ 0179 // END DEBUG. 0180 0181 lastPrecessJD = J2000; 0182 } 0183 0184 void StarObject::init(const DeepStarData *stardata) 0185 { 0186 double ra, dec, BV_Index; 0187 0188 ra = stardata->RA / 1000000.0; 0189 dec = stardata->Dec / 100000.0; 0190 setType(SkyObject::STAR); 0191 0192 if (stardata->V == 30000 && stardata->B != 30000) 0193 setMag((stardata->B - 1600) / 1000.0); // FIXME: Is it okay to make up stuff like this? 0194 else 0195 setMag(stardata->V / 1000.0); 0196 0197 setRA0(ra); 0198 setDec0(dec); 0199 setRA(ra); 0200 setDec(dec); 0201 0202 SpType[1] = '?'; 0203 SpType[0] = 'B'; 0204 if (stardata->B == 30000 || stardata->V == 30000) 0205 { 0206 // Unused value 0207 // BV_Index = -100; 0208 SpType[0] = '?'; 0209 } 0210 else 0211 { 0212 BV_Index = (stardata->B - stardata->V) / 1000.0; 0213 if (BV_Index > 0.0) SpType[0] = 'A'; 0214 if (BV_Index > 0.325) SpType[0] = 'F'; 0215 if (BV_Index > 0.575) SpType[0] = 'G'; 0216 if (BV_Index > 0.975) SpType[0] = 'K'; 0217 if (BV_Index > 1.6) SpType[0] = 'M'; 0218 } 0219 0220 PM_RA = stardata->dRA / 100.0; 0221 PM_Dec = stardata->dDec / 100.0; 0222 Parallax = 0.0; 0223 Multiplicity = 0; 0224 Variability = 0; 0225 updateID = updateNumID = 0; 0226 B = stardata->B / 1000.0; 0227 V = stardata->V / 1000.0; 0228 lastPrecessJD = J2000; 0229 } 0230 0231 void StarObject::setNames(const QString &name, const QString &name2) 0232 { 0233 QString lname; 0234 0235 setName(name); 0236 0237 setName2(name2); 0238 0239 if (hasName() && name.startsWith(QLatin1String("HD")) == false) 0240 { 0241 lname = name; 0242 if (hasName2()) 0243 lname += " (" + gname() + ')'; 0244 } 0245 else if (hasName2()) 0246 lname = gname(); 0247 setLongName(lname); 0248 } 0249 void StarObject::initPopupMenu(KSPopupMenu *pmenu) 0250 { 0251 #ifdef KSTARS_LITE 0252 Q_UNUSED(pmenu) 0253 #else 0254 pmenu->createStarMenu(this); 0255 #endif 0256 } 0257 0258 void StarObject::updateCoords(const KSNumbers *num, bool, const CachingDms *, const CachingDms *, bool) 0259 { 0260 //Correct for proper motion of stars. Determine RA and Dec offsets. 0261 //Proper motion is given im milliarcsec per year by the pmRA() and pmDec() functions. 0262 //That is numerically identical to the number of arcsec per millenium, so multiply by 0263 //KSNumbers::julianMillenia() to find the offsets in arcsec. 0264 0265 // Correction: The method below computes the proper motion before the 0266 // precession. If we precessed first then the direction of the proper 0267 // motion correction would depend on how far we've precessed. -jbb 0268 #ifdef PROFILE_UPDATECOORDS 0269 std::clock_t start, stop; 0270 start = std::clock(); 0271 #endif 0272 CachingDms saveRA = ra0(), saveDec = dec0(); 0273 CachingDms newRA, newDec; 0274 0275 getIndexCoords(num, newRA, newDec); 0276 0277 setRA0(newRA); 0278 setDec0(newDec); 0279 SkyPoint::updateCoords(num); 0280 setRA0(saveRA); 0281 setDec0(saveDec); 0282 0283 #ifdef PROFILE_UPDATECOORDS 0284 stop = std::clock(); 0285 updateCoordsCpuTime += double(stop - start) / double(CLOCKS_PER_SEC); 0286 ++starsUpdated; 0287 #endif 0288 } 0289 0290 bool StarObject::getIndexCoords(const KSNumbers *num, CachingDms &ra, CachingDms &dec) 0291 { 0292 static double pmms; 0293 0294 // =================== NOTE: CODE DUPLICATION ==================== 0295 // If you modify this, please also modify the other getIndexCoords 0296 // =============================================================== 0297 // 0298 // Reason for code duplication is as follows: 0299 // 0300 // This method is designed to use CachingDms, i.e. we know we are 0301 // going to use the sine and cosine of the returned values. 0302 // 0303 // The other method is designed to avoid CachingDms and try to 0304 // compute as little trigonometry as possible when the ra/dec has 0305 // to be returned in double (used in SkyMesh::indexStar() for 0306 // example) 0307 // 0308 // Thus, the philosophy of writing code is different. Granted, we 0309 // don't need to optimize for the smaller star catalogs (which use 0310 // SkyMesh::indexStar()), but it is nevertheless a good idea, 0311 // given that getIndexCoords() shows up in callgrind as one of the 0312 // slightly more expensive operations. 0313 0314 // Old, Incorrect Proper motion Computation. We retain this in a 0315 // comment because we might want to use it to come up with a 0316 // linear approximation that's faster. 0317 // double dra = pmRA() * num->julianMillenia() / ( cos( dec0().radians() ) * 3600.0 ); 0318 // double ddec = pmDec() * num->julianMillenia() / 3600.0; 0319 0320 0321 pmms = pmMagnitudeSquared(); 0322 0323 if (std::isnan(pmms) || pmms * num->julianMillenia() * num->julianMillenia() < .01) 0324 { 0325 // Ignore corrections 0326 ra = ra0(); 0327 dec = dec0(); 0328 return false; 0329 } 0330 0331 /* 0332 0333 // Proper Motion Correction should be implemented as motion along a great 0334 // circle passing through the given (ra0, dec0) in a direction of 0335 // atan2( pmRA(), pmDec() ) to an angular distance given by the Magnitude of 0336 // PM times the number of Julian millenia since J2000.0 0337 0338 double pm = pmMagnitude() * num->julianMillenia(); // Proper Motion in arcseconds 0339 0340 double dir0 = ((pm > 0) ? atan2(pmRA(), pmDec()) : atan2(-pmRA(), -pmDec())); // Bearing, in radian 0341 0342 if (pm < 0) 0343 pm = -pm; 0344 0345 double dst = (pm * M_PI / (180.0 * 3600.0)); 0346 // double phi = M_PI / 2.0 - dec0().radians(); 0347 0348 // Note: According to callgrind, dms::dms() + dms::setRadians() 0349 // takes ~ 40 CPU cycles, whereas, the advantage afforded by using 0350 // sincos() instead of sin() and cos() calls seems to be about 30 0351 // CPU cycles. 0352 0353 // So it seems like it is not worth turning dir0 and dst into dms 0354 // objects and using SinCos(). However, caching the values of sin 0355 // and cos if we are going to reuse them avoids expensive (~120 0356 // CPU cycle) recomputation! 0357 CachingDms lat1, dtheta; 0358 double sinDst = sin(dst), cosDst = cos(dst); 0359 lat1.setUsing_asin(dec0().sin() * cosDst + dec0().cos() * sinDst * cos(dir0)); 0360 dtheta.setUsing_atan2(sin(dir0) * sinDst * dec0().cos(), cosDst - dec0().sin() * lat1.sin()); 0361 0362 ra = ra0() + dtheta; // Use operator + to avoid trigonometry 0363 dec = lat1; // Need variable lat1 because dec may refer to dec0, so cannot construct result in-place 0364 0365 */ 0366 0367 // Use the formula given in Seidelmann (Explanatory Supplement to 0368 // the Astronomical Almanac) instead. The formulas used here are 0369 // the combination of (3.23-1), (3.23-3), (3.23-5). Not only does 0370 // it reduce trigonometry use, it is more likely to be correct 0371 // than the stuff we came up with on our own above 0372 0373 // Although it is not explained in the above reference, a formula 0374 // that reduces to α' = α + μ_α * t must have μ_α be the rate of 0375 // change of the angle at the center of the declination circle, 0376 // and not the rate of change of arclength, i.e. μ_α must _not_ 0377 // include the cos(δ) factor. I have checked that the formulas 0378 // used here do reduce to the above, and therefore expect μ_α = 0379 // pmRa / cos(δ) 0380 0381 double cosDec, sinDec, cosRa, sinRa; 0382 double scale = num->julianMillenia() * (M_PI / (180.0 * 3600.0)); 0383 dec0().SinCos(sinDec, cosDec); 0384 ra0().SinCos(sinRa, cosRa); 0385 0386 // Note: Below assumes that pmRA is already pre-scaled by cos(delta), as it is for Hipparcos 0387 double net_pmRA = pmRA() * scale, net_pmDec = pmDec() * scale; 0388 0389 double x0 = cosDec * cosRa, y0 = cosDec * sinRa, z0 = sinDec; 0390 double dX = - net_pmRA * sinRa - net_pmDec * sinDec * cosRa; 0391 double dY = net_pmRA * cosRa - net_pmDec * sinDec * sinRa; 0392 double dZ = net_pmDec * cosDec; 0393 double x = x0 + dX, y = y0 + dY, z = z0 + dZ; 0394 0395 ra.setUsing_atan2(y, x); 0396 0397 // Note: dec = asin(z) is a poor choice, because we aren't 0398 // guaranteed that (x, y, z) lies on the unit sphere due to the 0399 // first-order approximation. Therefore, we must "project" out any 0400 // change in the length of the vector to get our best estimate, 0401 // and this is achieved by using atan. In fact atan gives the 0402 // least-squares estimate for an angle given both its sin and 0403 // cosine components. 0404 dec.setUsing_atan2(z, sqrt(x * x + y * y)); 0405 0406 return true; 0407 } 0408 0409 bool StarObject::getIndexCoords(const KSNumbers *num, double *ra, double *dec) 0410 { 0411 static double pmms; 0412 0413 // =================== NOTE: CODE DUPLICATION ==================== 0414 // If you modify this, please also modify the other getIndexCoords 0415 // =============================================================== 0416 // 0417 // Reason for code duplication is as follows: 0418 // 0419 // This method is designed to avoid CachingDms and try to compute 0420 // as little trigonometry as possible when the ra/dec has to be 0421 // returned in double (used in SkyMesh::indexStar() for example) 0422 // 0423 // The other method is designed to use CachingDms, i.e. we know we 0424 // are going to use the sine and cosine of the returned values. 0425 // 0426 // Thus, the philosophy of writing code is different. Granted, we 0427 // don't need to optimize for the smaller star catalogs (which use 0428 // SkyMesh::indexStar()), but it is nevertheless a good idea, 0429 // given that getIndexCoords() shows up in callgrind as one of the 0430 // slightly more expensive operations. 0431 0432 // Old, Incorrect Proper motion Computation. We retain this in a 0433 // comment because we might want to use it to come up with a 0434 // linear approximation that's faster. 0435 // double dra = pmRA() * num->julianMillenia() / ( cos( dec0().radians() ) * 3600.0 ); 0436 // double ddec = pmDec() * num->julianMillenia() / 3600.0; 0437 0438 // Proper Motion Correction should be implemented as motion along a great 0439 // circle passing through the given (ra0, dec0) in a direction of 0440 // atan2( pmRA(), pmDec() ) to an angular distance given by the Magnitude of 0441 // PM times the number of Julian millenia since J2000.0 0442 0443 pmms = pmMagnitudeSquared(); 0444 0445 if (std::isnan(pmms) || pmms * num->julianMillenia() * num->julianMillenia() < .01) 0446 { 0447 // Ignore corrections 0448 *ra = ra0().Degrees(); 0449 *dec = dec0().Degrees(); 0450 return false; 0451 } 0452 0453 /* 0454 double pm = pmMagnitude() * num->julianMillenia(); // Proper Motion in arcseconds 0455 0456 double dir0 = ((pm > 0) ? atan2(pmRA(), pmDec()) : atan2(-pmRA(), -pmDec())); // Bearing, in radian 0457 0458 (pm < 0) && (pm = -pm); 0459 0460 double dst = (pm * M_PI / (180.0 * 3600.0)); 0461 // double phi = M_PI / 2.0 - dec0().radians(); 0462 0463 // Note: According to callgrind, dms::dms() + dms::setRadians() 0464 // takes ~ 40 CPU cycles, whereas, the advantage afforded by using 0465 // sincos() instead of sin() and cos() calls seems to be about 30 0466 // CPU cycles. 0467 0468 // So it seems like it is not worth turning dir0 and dst into dms 0469 // objects and using SinCos(). However, caching the values of sin 0470 // and cos if we are going to reuse them avoids expensive (~120 0471 // CPU cycle) recomputation! 0472 dms lat1, dtheta; 0473 double sinDst = sin(dst), cosDst = cos(dst); 0474 double sinLat1 = dec0().sin() * cosDst + dec0().cos() * sinDst * cos(dir0); 0475 lat1.setRadians(asin(sinLat1)); 0476 dtheta.setRadians(atan2(sin(dir0) * sinDst * dec0().cos(), cosDst - dec0().sin() * sinLat1)); 0477 0478 // Using dms instead, to ensure that the numbers are in the right range. 0479 dms finalRA(ra0().Degrees() + dtheta.Degrees()); 0480 0481 *ra = finalRA.Degrees(); 0482 *dec = lat1.Degrees(); 0483 */ 0484 0485 0486 // Although it is not explained in the above reference, a formula 0487 // that reduces to α' = α + μ_α * t must have μ_α be the rate of 0488 // change of the angle at the center of the declination circle, 0489 // and not the rate of change of arclength, i.e. μ_α must _not_ 0490 // include the cos(δ) factor. I have checked that the formulas 0491 // used in Seidelmann do reduce to the above, and therefore expect 0492 // μ_α = pmRa / cos(δ). Therefore, I'm absorbing the factor in the 0493 // implementation here. 0494 0495 double cosDec, sinDec, cosRa, sinRa; 0496 double scale = num->julianMillenia() * (M_PI / (180.0 * 3600.0)); 0497 dec0().SinCos(sinDec, cosDec); 0498 ra0().SinCos(sinRa, cosRa); 0499 0500 // Note: Below assumes that pmRA is already pre-scaled by cos(delta), as it is for Hipparcos 0501 double net_pmRA = pmRA() * scale, net_pmDec = pmDec() * scale; 0502 0503 double x0 = cosDec * cosRa, y0 = cosDec * sinRa, z0 = sinDec; 0504 double dX = - net_pmRA * sinRa - net_pmDec * sinDec * cosRa; 0505 double dY = net_pmRA * cosRa - net_pmDec * sinDec * sinRa; 0506 double dZ = net_pmDec * cosDec; 0507 double x = x0 + dX, y = y0 + dY, z = z0 + dZ; 0508 0509 dms alpha, delta; 0510 alpha.setRadians(atan2(y, x)); 0511 0512 // Note: dec = asin(z) is a poor choice, because we aren't 0513 // guaranteed that (x, y, z) lies on the unit sphere due to the 0514 // first-order approximation. Therefore, we must "project" out any 0515 // change in the length of the vector to get our best estimate, 0516 // and this is achieved by using atan. In fact atan gives the 0517 // least-squares estimate for an angle given both its sin and 0518 // cosine components. 0519 delta.setRadians(atan2(z, sqrt(x * x + y * y))); 0520 *ra = alpha.reduce().Degrees(); 0521 *dec = delta.Degrees(); 0522 0523 return true; 0524 } 0525 0526 void StarObject::JITupdate() 0527 { 0528 static KStarsData *data = KStarsData::Instance(); 0529 0530 if (updateNumID != data->updateNumID()) 0531 { 0532 // TODO: This can be optimized and reorganized further in a better manner. 0533 // Maybe we should do this only for stars, since this is really a slow step only for stars 0534 Q_ASSERT(std::isfinite(lastPrecessJD)); 0535 0536 if (Options::alwaysRecomputeCoordinates() || (Options::useRelativistic() && checkBendLight()) || 0537 std::abs(lastPrecessJD - data->updateNum()->getJD()) >= 0.00069444) // Update is once per solar minute 0538 { 0539 // Short circuit right here, if recomputing coordinates is not required. NOTE: POTENTIALLY DANGEROUS 0540 updateCoords(data->updateNum()); 0541 } 0542 0543 updateNumID = data->updateNumID(); 0544 } 0545 EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0546 updateID = data->updateID(); 0547 } 0548 0549 QString StarObject::sptype(void) const 0550 { 0551 return QString(QByteArray(SpType, 2)); 0552 } 0553 0554 char StarObject::spchar() const 0555 { 0556 return SpType[0]; 0557 } 0558 0559 QString StarObject::gname(bool useGreekChars) const 0560 { 0561 if (!name2().isEmpty()) 0562 return greekLetter(useGreekChars) + ' ' + constell(); 0563 else 0564 return QString(); 0565 } 0566 0567 QString StarObject::greekLetter(bool gchar) const 0568 { 0569 QString code = name2().left(3); 0570 QString letter = code; //in case genitive name is *not* a Greek letter 0571 int alpha = 0x03B1; 0572 0573 auto checkAndGreekify = [&code, gchar, alpha, &letter](const QString &abbrev, int unicodeOffset, 0574 const QString &expansion) { 0575 if (code == abbrev) 0576 gchar ? letter = QString(QChar(alpha + unicodeOffset)) : letter = expansion; 0577 }; 0578 0579 checkAndGreekify("alp", 0, i18n("alpha")); 0580 checkAndGreekify("bet", 1, i18n("beta")); 0581 checkAndGreekify("gam", 2, i18n("gamma")); 0582 checkAndGreekify("del", 3, i18n("delta")); 0583 checkAndGreekify("eps", 4, i18n("epsilon")); 0584 checkAndGreekify("zet", 5, i18n("zeta")); 0585 checkAndGreekify("eta", 6, i18n("eta")); 0586 checkAndGreekify("the", 7, i18n("theta")); 0587 checkAndGreekify("iot", 8, i18n("iota")); 0588 checkAndGreekify("kap", 9, i18n("kappa")); 0589 checkAndGreekify("lam", 10, i18n("lambda")); 0590 checkAndGreekify("mu ", 11, i18n("mu")); 0591 checkAndGreekify("nu ", 12, i18n("nu")); 0592 checkAndGreekify("xi ", 13, i18n("xi")); 0593 checkAndGreekify("omi", 14, i18n("omicron")); 0594 checkAndGreekify("pi ", 15, i18n("pi")); 0595 checkAndGreekify("rho", 16, i18n("rho")); 0596 //there are two unicode symbols for sigma; 0597 //skip the first one, the second is more widely used 0598 checkAndGreekify("sig", 18, i18n("sigma")); 0599 checkAndGreekify("tau", 19, i18n("tau")); 0600 checkAndGreekify("ups", 20, i18n("upsilon")); 0601 checkAndGreekify("phi", 21, i18n("phi")); 0602 checkAndGreekify("chi", 22, i18n("chi")); 0603 checkAndGreekify("psi", 23, i18n("psi")); 0604 checkAndGreekify("ome", 24, i18n("omega")); 0605 0606 if (name2().length() && name2().mid(3, 1) != " ") 0607 letter += '[' + name2().mid(3, 1) + ']'; 0608 0609 return letter; 0610 } 0611 0612 // FIXME: Move this somewhere else, make this static, and give it a better name. 0613 // Mostly for code cleanliness. Also, try to put it in a DB. 0614 QString StarObject::constell() const 0615 { 0616 QString code = name2().mid(4, 3); 0617 0618 return KSUtils::constGenetiveFromAbbrev(code); 0619 } 0620 0621 // The two routines below seem overly complicated but at least they are doing 0622 // the right thing now. Please resist the temptation to simplify them unless 0623 // you are prepared to ensure there is no ugly label overlap for all 8 cases 0624 // they deal with ( drawName x DrawMag x star-has-name). -jbb 0625 0626 QString StarObject::nameLabel(bool drawName, bool drawMag) const 0627 { 0628 QString sName; 0629 0630 if (drawName) 0631 { 0632 QString translation = translatedName(); 0633 if (translation != i18n("star") && !translation.startsWith("HD")) 0634 sName = translation; 0635 else if (!gname().trimmed().isEmpty()) 0636 sName = gname(true); 0637 else 0638 { 0639 if (drawMag) 0640 return ('[' + QLocale().toString(mag(), 'f', 1) + "m]"); 0641 } 0642 if (!drawMag) 0643 return sName; 0644 else 0645 return sName + " [" + QLocale().toString(mag(), 'f', 1) + "m]"; 0646 } 0647 return ('[' + QLocale().toString(mag(), 'f', 1) + "m]"); 0648 } 0649 0650 //If this works, we can maybe get rid of customLabel() and nameLabel()?? 0651 QString StarObject::labelString() const 0652 { 0653 return nameLabel(Options::showStarNames(), Options::showStarMagnitudes()); 0654 } 0655 0656 double StarObject::labelOffset() const 0657 { 0658 return (6. + 0.5 * (5.0 - mag()) + 0.01 * (Options::zoomFactor() / 500.)); 0659 } 0660 0661 SkyObject::UID StarObject::getUID() const 0662 { 0663 // mag takes 10 bit 0664 SkyObject::UID m = mag() * 10; 0665 if (m < 0) 0666 m = 0; 0667 0668 // Both RA & dec fits in 24-bits 0669 SkyObject::UID ra = ra0().Degrees() * 36000; 0670 SkyObject::UID dec = (ra0().Degrees() + 91) * 36000; 0671 0672 Q_ASSERT("Magnitude is expected to fit into 10bits" && m >= 0 && m < (1 << 10)); 0673 Q_ASSERT("RA should fit into 24bits" && ra >= 0 && ra < (1 << 24)); 0674 Q_ASSERT("Dec should fit into 24bits" && dec >= 0 && dec < (1 << 24)); 0675 0676 return (SkyObject::UID_STAR << 60) | (m << 48) | (ra << 24) | dec; 0677 }