• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Stellarium / stellarium / 15291801018

28 May 2025 04:52AM UTC coverage: 11.931% (-0.02%) from 11.951%
15291801018

push

github

alex-w
Added new set of navigational stars (XIX century)

0 of 6 new or added lines in 2 files covered. (0.0%)

14124 existing lines in 74 files now uncovered.

14635 of 122664 relevant lines covered (11.93%)

18291.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

2.89
/src/core/modules/Comet.cpp
1
/*
2
 * Stellarium
3
 * Copyright (C) 2010 Bogdan Marinov
4
 * Copyright (C) 2014 Georg Zotti (Tails)
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335, USA.
19
 */
20
 
21
#include "Comet.hpp"
22
#include "Orbit.hpp"
23
#include "SolarSystem.hpp"
24

25
#include "StelApp.hpp"
26
#include "StelCore.hpp"
27
#include "StelPainter.hpp"
28

29
#include "StelTexture.hpp"
30
#include "StelToneReproducer.hpp"
31
#include "StelTranslator.hpp"
32
#include "StelUtils.hpp"
33
#include "StelMovementMgr.hpp"
34
#include "StelModuleMgr.hpp"
35
#include "LandscapeMgr.hpp"
36

37
#include <QDebug>
38
#include <QElapsedTimer>
39

40
// for compute tail shape
41
#define COMET_TAIL_SLICES 16u // segments around the perimeter
42
#define COMET_TAIL_STACKS 16u // cuts along the rotational axis
43

44
// These are to avoid having index arrays for each comet when all are equal.
45
bool Comet::createTailIndices=true;
46
bool Comet::createTailTextureCoords=true;
47
StelTextureSP Comet::comaTexture;
48
StelTextureSP Comet::tailTexture;
49
QVector<Vec2f> Comet::tailTexCoordArr; // computed only once for all Comets.
50
QVector<unsigned short> Comet::tailIndices; // computed only once for all Comets.
51

UNCOV
52
Comet::Comet(const QString& englishName,
×
53
             double radius,
54
             double oblateness,
55
             Vec3f halocolor,
56
             float albedo,
57
             float roughness,
58
             float outgas_intensity,
59
             float outgas_falloff,
60
             const QString& atexMapName,
61
             const QString& aobjModelName,
62
             posFuncType coordFunc,
63
             KeplerOrbit* orbitPtr,
64
             OsculatingFunctType *osculatingFunc,
65
             bool acloseOrbit,
66
             bool hidden,
67
             const QString& pTypeStr,
68
             float dustTailWidthFact,
69
             float dustTailLengthFact,
UNCOV
70
             float dustTailBrightnessFact)
×
71
        : Planet (englishName,
72
                  radius,
73
                  oblateness,
74
                  halocolor,
75
                  albedo,
76
                  roughness,
77
                  atexMapName,
78
                  "", // no normalmap.
79
                  "", // no horizon map
80
                  aobjModelName,
81
                  coordFunc,
82
                  orbitPtr,
83
                  osculatingFunc,
84
                  acloseOrbit,
85
                  hidden,
86
                  false, //No atmosphere
87
                  true, //halo
88
                  pTypeStr),
UNCOV
89
          slopeParameter(-10.f), // -10 == uninitialized: used in getVMagnitude()
×
90
          isCometFragment(false),
×
91
          iauDesignation(QString()),
×
92
          extraDesignations(),
×
93
          extraDesignationsHtml(),
×
94
          tailFactors(-1., -1.), // mark "invalid"
×
95
          tailActive(false),
×
96
          tailBright(false),
×
97
          deltaJDEtail(15.0*StelCore::JD_MINUTE), // update tail geometry every 15 minutes only
×
98
          lastJDEtail(0.0),
×
99
          dustTailWidthFactor(dustTailWidthFact),
×
100
          dustTailLengthFactor(dustTailLengthFact),
×
101
          dustTailBrightnessFactor(dustTailBrightnessFact),
×
102
          intensityFovScale(1.0f),
×
103
          intensityMinFov(0.001f), // when zooming in further, Coma is no longer visible.
×
104
          intensityMaxFov(0.010f) // when zooming out further, Coma is fully visible (when enabled).
×
105
{
106
        this->outgas_intensity =outgas_intensity;
×
107
        this->outgas_falloff   =outgas_falloff;
×
108

109
        gastailVertexArr.clear();
×
110
        dusttailVertexArr.clear();
×
UNCOV
111
        comaVertexArr.clear();
×
112
        gastailColorArr.clear();
×
113
        dusttailColorArr.clear();
×
114

115
        //TODO: Name processing?
116
}
×
117

UNCOV
118
Comet::~Comet()
×
119
{
UNCOV
120
}
×
121

UNCOV
122
void Comet::setAbsoluteMagnitudeAndSlope(const float magnitude, const float slope)
×
123
{
UNCOV
124
        if ((slope < -2.5f) || (slope > 25.0f))
×
125
        {
126
                // Slope G can become slightly smaller than 0. -10 is mark of invalidity.
127
                qWarning().noquote() << "Suspect slope parameter value" << slope << "for comet" << getEnglishName() << "(rarely exceeding -1...20)";
×
UNCOV
128
                return;
×
129
        }
130
        absoluteMagnitude = magnitude;
×
131
        slopeParameter = slope;
×
132
}
133

134
void Comet::translateName(const StelTranslator &translator)
×
135
{
UNCOV
136
        static const QRegularExpression cometNamePattern("^(.+)[(](.+)[)]\\s*$");
×
137
        QRegularExpressionMatch matchCometName = cometNamePattern.match(englishName);
×
UNCOV
138
        if (matchCometName.hasMatch())
×
139
                nameI18 = QString("%1(%2)").arg(matchCometName.captured(1),translator.qtranslate(matchCometName.captured(2), "comet"));
×
140
        else
141
                nameI18 = translator.qtranslate(englishName, "comet");
×
142
}
×
143

144
QString Comet::getInfoStringName(const StelCore *core, const InfoStringGroup& flags) const
×
145
{
146
        Q_UNUSED(core) Q_UNUSED(flags)
147
        QString str;
×
UNCOV
148
        QTextStream oss(&str);
×
149

150
        oss << "<h2>";
×
151
        oss << getNameI18n(); // UI translation can differ from sky translation
×
152

153
        if (!iauDesignation.isEmpty() &&  !getNameI18n().contains(iauDesignation))
×
154
                oss << QString(" - %1").arg(iauDesignation);
×
155

156
        if (!getExtraDesignations().isEmpty())
×
157
                oss << QString(" - %1").arg(extraDesignationsHtml.join(" - "));
×
158

159
        if (sphereScale != 1.)
×
160
        {
UNCOV
161
                oss.setRealNumberNotation(QTextStream::FixedNotation);
×
162
                oss.setRealNumberPrecision(1);
×
UNCOV
163
                oss << QString::fromUtf8(" (\xC3\x97") << sphereScale << ")";
×
164
        }
165
        oss << "</h2>";
×
166

UNCOV
167
        return str;
×
168
}
×
169

170
QString Comet::getInfoStringAbsoluteMagnitude(const StelCore *core, const InfoStringGroup& flags) const
×
171
{
172
        Q_UNUSED(core)
173
        QString str;
×
UNCOV
174
        QTextStream oss(&str);
×
UNCOV
175
        if (flags&AbsoluteMagnitude)
×
176
        {
177
                //TODO: Make sure absolute magnitude is a sane value
178
                //If the two parameter magnitude system is not used, don't display this
179
                //value. (Using radius/albedo doesn't make any sense for comets.)
180
                // Note that slope parameter can be <0 (down to -2?), so -10 is now used for "uninitialized"
UNCOV
181
                if (slopeParameter >= -9.9f)
×
UNCOV
182
                        oss << QString("%1: %2<br/>").arg(q_("Absolute Magnitude")).arg(absoluteMagnitude, 0, 'f', 2);
×
183
        }
184

185
        return str;
×
UNCOV
186
}
×
187

188
QString Comet::getInfoStringSize(const StelCore *core, const InfoStringGroup &flags) const
×
189
{
190
        // TRANSLATORS: Unit of measure for distance - kilometers
191
        QString km = qc_("km", "distance");
×
192
        // TRANSLATORS: Unit of measure for distance - milliones kilometers
UNCOV
193
        QString Mkm = qc_("M km", "distance");
×
194
        const bool withDecimalDegree = StelApp::getInstance().getFlagShowDecimalDegrees();
×
UNCOV
195
        QString str;
×
196
        QTextStream oss(&str);
×
197

198
        if (flags&Size)
×
199
        {
200
                // Given the very irregular shape, other terminology like "equatorial radius" do not make much sense.
201
                oss << QString("<br/>%1: %2 %3<br/>").arg(q_("Core diameter"), QString::number(AU * 2.0 * getEquatorialRadius(), 'f', 1) , qc_("km", "distance"));
×
202
        }
UNCOV
203
        if ((flags&Size) && (tailFactors[0]>0.0f))
×
204
        {
205
                // GZ: Add estimates for coma diameter and tail length.
206
                QString comaEst = q_("Coma diameter (estimate)");
×
UNCOV
207
                const double coma = std::floor(static_cast<double>(tailFactors[0])*AU/1000.0)*1000.0;
×
UNCOV
208
                const double tail = static_cast<double>(tailFactors[1])*AU;
×
209
                const double distanceKm = AU * getJ2000EquatorialPos(core).norm();
×
210
                // Try to estimate tail length in degrees.
211
                // TODO: Take projection effect into account!
212
                // The estimates here assume that the tail is seen from the side.
UNCOV
213
                QString comaDeg, tailDeg;
×
UNCOV
214
                if (withDecimalDegree)
×
215
                {
216
                        comaDeg = StelUtils::radToDecDegStr(atan(coma/distanceKm),4,false,true);
×
217
                        tailDeg = StelUtils::radToDecDegStr(atan(tail/distanceKm),4,false,true);
×
218
                }
219
                else
220
                {
UNCOV
221
                        comaDeg = StelUtils::radToDmsStr(atan(coma/distanceKm));
×
UNCOV
222
                        tailDeg = StelUtils::radToDmsStr(atan(tail/distanceKm));
×
223
                }
224
                if (coma>1e6)
×
225
                        oss << QString("%1: %2 %3 (%4)<br/>").arg(comaEst, QString::number(coma*1e-6, 'G', 3), Mkm, comaDeg);
×
226
                else
227
                        oss << QString("%1: %2 %3 (%4)<br/>").arg(comaEst, QString::number(coma, 'f', 0), km, comaDeg);
×
228
                oss << QString("%1: %2 %3 (%4)<br/>").arg(q_("Gas tail length (estimate)"), QString::number(tail*1e-6, 'G', 3), Mkm, tailDeg);
×
UNCOV
229
        }
×
230
        return str;
×
231
}
×
232

233
QString Comet::getInfoStringExtra(const StelCore *core, const InfoStringGroup &flags) const
×
234
{
235
        Q_UNUSED(core)
236
        QString str;
×
UNCOV
237
        QTextStream oss(&str);
×
UNCOV
238
        if (flags&Extra)
×
239
        {
240
                if (!discoveryDate.isEmpty())
×
241
                        oss << QString("%1: %2<br/>").arg(q_("Discovered"), getDiscoveryCircumstances());
×
242
        }
243
        return str;
×
244
}
×
245

246
QVariantMap Comet::getInfoMap(const StelCore *core) const
×
247
{
UNCOV
248
        QVariantMap map = Planet::getInfoMap(core);
×
249
        map.insert("tail-length-km", tailFactors[1]*AUf);
×
UNCOV
250
        map.insert("coma-diameter-km", tailFactors[0]*AUf);
×
251

252
        return map;
×
253
}
×
254

255
double Comet::getSiderealPeriod() const
×
256
{
UNCOV
257
        const double semiMajorAxis=static_cast<KeplerOrbit*>(orbitPtr)->getSemimajorAxis();
×
258
        return ((semiMajorAxis>0) ? KeplerOrbit::calculateSiderealPeriod(semiMajorAxis, 1.0) : 0.);
×
259
}
260

261

262
float Comet::getVMagnitude(const StelCore* core) const
×
263
{
264
        return getVMagnitude(core, 1.0);
×
265
}
266

267
float Comet::getVMagnitude(const StelCore* core, const double eclipseFactor) const
×
268
{
269
        Q_UNUSED(eclipseFactor)
270
        //If the two parameter system is not used,
271
        //use the default radius/albedo mechanism
UNCOV
272
        if (slopeParameter < -9.0f)
×
273
        {
274
                return Planet::getVMagnitude(core, 1.);
×
275
        }
276

277
        //Calculate distances
UNCOV
278
        Vec3d observerHeliocentricPosition;
×
279
        if (core->getCurrentPlanet()->getPlanetType()==Planet::isObserver)
×
280

UNCOV
281
                observerHeliocentricPosition = Vec3d(0.f,0.f,0.f);
×
282
        else
UNCOV
283
                observerHeliocentricPosition = core->getObserverHeliocentricEclipticPos();
×
284

UNCOV
285
        const Vec3d& cometHeliocentricPosition = getHeliocentricEclipticPos();
×
286
        const float cometSunDistance = static_cast<float>(cometHeliocentricPosition.norm());
×
UNCOV
287
        const float observerCometDistance = static_cast<float>((observerHeliocentricPosition - cometHeliocentricPosition).norm());
×
288

289
        //Calculate apparent magnitude
290
        //Sources: http://www.clearskyinstitute.com/xephem/help/xephem.html#mozTocId564354
291
        //(XEphem manual, section 7.1.2.3 "Magnitude models"), also
292
        //http://www.ayton.id.au/gary/Science/Astronomy/Ast_comets.htm#Comet%20facts:
293
        // GZ: Note that Meeus, Astr.Alg.1998 p.231, has m=absoluteMagnitude+5log10(observerCometDistance) + kappa*log10(cometSunDistance)
294
        // with kappa typically 5..15. MPC provides Slope parameter. So we should expect to have slopeParameter (a word only used for minor planets!) for our comets 2..6
295
        return absoluteMagnitude + 5.f * std::log10(observerCometDistance) + 2.5f * slopeParameter * std::log10(cometSunDistance);
×
296
}
297

298
void Comet::update(int deltaTime)
×
299
{
UNCOV
300
        Planet::update(deltaTime);
×
301

302
        //calculate FOV fade value, linear fade between intensityMaxFov and intensityMinFov
UNCOV
303
        const float vfov = static_cast<float>(StelApp::getInstance().getCore()->getMovementMgr()->getCurrentFov());
×
UNCOV
304
        intensityFovScale = qBound(0.25f,(vfov - intensityMinFov) / (intensityMaxFov - intensityMinFov),1.0f);
×
305

306
        // The rest deals with updating tail geometries and brightness
307
        StelCore* core=StelApp::getInstance().getCore();
×
UNCOV
308
        double dateJDE=core->getJDE();
×
309

310
        if (!static_cast<KeplerOrbit*>(orbitPtr)->objectDateGoodEnoughForOrbits(dateJDE)) return; // don't do anything if out of useful date range. This allows having hundreds of comet elements.
×
311

312
        //GZ: I think we can make deltaJDtail adaptive, depending on distance to sun! For some reason though, this leads to a crash!
313
        //deltaJDtail=StelCore::JD_SECOND * qBound(1.0, eclipticPos.length(), 20.0);
314

315
        if (fabs(lastJDEtail-dateJDE)>deltaJDEtail)
×
316
        {
UNCOV
317
                lastJDEtail=dateJDE;
×
318

319
                if (static_cast<KeplerOrbit*>(orbitPtr)->getUpdateTails()){
×
320
                        // Compute lengths and orientations from orbit object, but only if required.
UNCOV
321
                        tailFactors=getComaDiameterAndTailLengthAU();
×
322

323
                        // Note that we use a diameter larger than what the formula returns. A scale factor of 1.2 is ad-hoc/empirical (GZ), but may look better.
UNCOV
324
                        computeComa(1.0f*tailFactors[0]); // TBD: APPARENTLY NO SCALING? REMOVE 1.0 and note above.
×
325

UNCOV
326
                        tailActive = (tailFactors[1] > tailFactors[0]); // Inhibit tails drawing if too short. Would be nice to include geometric projection angle, but this is too costly.
×
327

UNCOV
328
                        if (tailActive)
×
329
                        {
UNCOV
330
                                float gasTailEndRadius=qMax(tailFactors[0], 0.025f*tailFactors[1]) ; // This avoids too slim gas tails for bright comets like Hale-Bopp.
×
331
                                float gasparameter=gasTailEndRadius*gasTailEndRadius/(2.0f*tailFactors[1]); // parabola formula: z=r²/2p, so p=r²/2z
×
332
                                // The dust tail is thicker and usually shorter. The factors can be configured in the elements.
333
                                float dustparameter=gasTailEndRadius*gasTailEndRadius*dustTailWidthFactor*dustTailWidthFactor/(2.0f*dustTailLengthFactor*tailFactors[1]);
×
334

335
                                // Find valid parameters to create paraboloid vertex arrays: dustTail, gasTail.
336
                                computeParabola(gasparameter, gasTailEndRadius, -0.5f*gasparameter, gastailVertexArr,  tailTexCoordArr, tailIndices);
×
337
                                //gastailColorArr.fill(Vec3f(0.3,0.3,0.3), gastailVertexArr.length());
338
                                // Now we make a skewed parabola. Skew factor (xOffset, last arg) is rather ad-hoc/empirical. TBD later: Find physically correct solution.
UNCOV
339
                                computeParabola(dustparameter, dustTailWidthFactor*gasTailEndRadius, -0.5f*dustparameter, dusttailVertexArr, tailTexCoordArr, tailIndices, 25.0f*static_cast<float>(static_cast<KeplerOrbit*>(orbitPtr)->getVelocity().norm()));
×
340
                                //dusttailColorArr.fill(Vec3f(0.3,0.3,0.3), dusttailVertexArr.length());
341

342

343
                                // 2014-08 for 0.13.1 Moved from drawTail() to save lots of computation per frame (There *are* folks downloading all 730 MPC current comet elements...)
344
                                // Find rotation matrix from 0/0/1 to eclipticPosition: crossproduct for axis (normal vector), dotproduct for angle.
345
                                Vec3d eclposNrm=eclipticPos+aberrationPush; eclposNrm.normalize();
×
UNCOV
346
                                gasTailRot=Mat4d::rotation(Vec3d(0.0, 0.0, 1.0)^(eclposNrm), std::acos(Vec3d(0.0, 0.0, 1.0).dot(eclposNrm)) );
×
347

348
                                Vec3d velocity=static_cast<KeplerOrbit*>(orbitPtr)->getVelocity(); // [AU/d]
×
349
                                // This was a try to rotate a straight parabola somewhat away from the antisolar direction.
350
                                //Mat4d dustTailRot=Mat4d::rotation(eclposNrm^(-velocity), 0.15f*std::acos(eclposNrm.dot(-velocity))); // GZ: This scale factor of 0.15 is empirical from photos of Halley and Hale-Bopp.
351
                                // The curved tail is curved towards positive X. We first rotate around the Z axis into a direction opposite of the motion vector, then again the antisolar rotation applies.
352
                                // In addition, we let the dust tail already start with a light tilt.
UNCOV
353
                                dustTailRot=gasTailRot * Mat4d::zrotation(atan2(velocity[1], velocity[0]) + M_PI) * Mat4d::yrotation(5.0*velocity.norm());
×
354

355
                                // Rotate vertex arrays:
UNCOV
356
                                Vec3d* gasVertices= static_cast<Vec3d*>(gastailVertexArr.data());
×
357
                                Vec3d* dustVertices=static_cast<Vec3d*>(dusttailVertexArr.data());
×
358
                                for (unsigned short int i=0; i<COMET_TAIL_SLICES*COMET_TAIL_STACKS+1; ++i)
×
359
                                {
360
                                        gasVertices[i].transfo4d(gasTailRot);
×
UNCOV
361
                                        dustVertices[i].transfo4d(dustTailRot);
×
362
                                }
363
                        }
UNCOV
364
                        static_cast<KeplerOrbit*>(orbitPtr)->setUpdateTails(false); // don't update until position has been recalculated elsewhere
×
365
                }
366
        }
367

368
        // And also update magnitude and tail brightness/extinction here.
369
        const bool withAtmosphere=(core->getSkyDrawer()->getFlagHasAtmosphere());
×
370

UNCOV
371
        StelToneReproducer* eye = core->getToneReproducer();
×
372
        const float lum = core->getSkyDrawer()->surfaceBrightnessToLuminance(getVMagnitude(core)+13.0f); // How to calibrate?
×
373
        // Get the luminance scaled between 0 and 1
UNCOV
374
        float aLum =eye->adaptLuminanceScaled(lum);
×
375

376

377
        // To make comet more apparent in overviews, take field of view into account:
UNCOV
378
        const float fov=core->getProjection(core->getAltAzModelViewTransform())->getFov();
×
UNCOV
379
        if (fov>20)
×
UNCOV
380
                aLum*= (fov/20.0f);
×
381

382
        // Now inhibit tail drawing if still too dim.
383
        if (aLum<0.002f)
×
384
        {
385
                // Far too dim: don't even show tail...
386
                tailBright=false;
×
UNCOV
387
                return;
×
388
        } else
UNCOV
389
                tailBright=true;
×
390

391
        // Separate factors, but avoid overly bright tails. I limit to about 0.7 for overlapping both tails which should not exceed full-white.
392
        float gasMagFactor=qMin(0.9f*aLum, 0.7f);
×
UNCOV
393
        float dustMagFactor=qMin(dustTailBrightnessFactor*aLum, 0.7f);
×
394

395
        // TODO: Maybe make gas color distance dependent? (various typical ingredients outgas at different temperatures...)
UNCOV
396
        Vec3f gasColor(0.15f*gasMagFactor,0.35f*gasMagFactor,0.6f*gasMagFactor); // Orig color 0.15/0.15/0.6.
×
UNCOV
397
        Vec3f dustColor(dustMagFactor, dustMagFactor,0.6f*dustMagFactor);
×
398

399
        if (withAtmosphere)
×
400
        {
401
                Extinction extinction=core->getSkyDrawer()->getExtinction();
×
402

403
                // Not only correct the color values for extinction, but for twilight conditions, also make tail end less visible.
404
                // I consider sky brightness over 1cd/m^2 as reason to shorten tail.
405
                // Below this brightness, the tail brightness loss by this method is insignificant:
406
                // Just counting through the vertices might make a spiral appearance. Maybe even better than stackwise? Let's see...
UNCOV
407
                static LandscapeMgr *lMgr=GETSTELMODULE(LandscapeMgr);
×
408
                const float avgAtmLum=lMgr->getAtmosphereAverageLuminance();
×
409
                const float brightnessDecreasePerVertexFromHead=1.0f/(COMET_TAIL_SLICES*COMET_TAIL_STACKS)  * avgAtmLum;
×
UNCOV
410
                float brightnessPerVertexFromHead=1.0f;
×
411

UNCOV
412
                gastailColorArr.clear();
×
413
                dusttailColorArr.clear();
×
UNCOV
414
                for (int i=0; i<gastailVertexArr.size(); ++i)
×
415
                {
416
                        // Gastail extinction:
UNCOV
417
                        Vec3d vertAltAz=core->j2000ToAltAz(gastailVertexArr.at(i), StelCore::RefractionOn);
×
UNCOV
418
                        vertAltAz.normalize();
×
419
                        Q_ASSERT(fabs(vertAltAz.normSquared()-1.0) < 0.001);
×
420
                        float oneMag=0.0f;
×
421
                        extinction.forward(vertAltAz, &oneMag);
×
422
                        float extinctionFactor=std::pow(0.4f, oneMag); // drop of one magnitude: factor 2.5 or 40%
×
UNCOV
423
                        gastailColorArr.append(gasColor*extinctionFactor* brightnessPerVertexFromHead*intensityFovScale);
×
424

425
                        // dusttail extinction:
426
                        vertAltAz=core->j2000ToAltAz(dusttailVertexArr.at(i), StelCore::RefractionOn);
×
UNCOV
427
                        vertAltAz.normalize();
×
UNCOV
428
                        Q_ASSERT(fabs(vertAltAz.normSquared()-1.0) < 0.001);
×
429
                        oneMag=0.0f;
×
430
                        extinction.forward(vertAltAz, &oneMag);
×
431
                        extinctionFactor=std::pow(0.4f, oneMag); // drop of one magnitude: factor 2.5 or 40%
×
432
                        dusttailColorArr.append(dustColor*extinctionFactor * brightnessPerVertexFromHead*intensityFovScale);
×
433

434
                        brightnessPerVertexFromHead-=brightnessDecreasePerVertexFromHead;
×
435
                }
436
        }
437
        else // no atmosphere: set all vertices to same brightness.
438
        {
439
                gastailColorArr.fill(gasColor  *intensityFovScale, gastailVertexArr.length());
×
440
                dusttailColorArr.fill(dustColor*intensityFovScale, dusttailVertexArr.length());
×
441
        }
442
        //qDebug() << "Comet " << getEnglishName() <<  "JDE: " << date << "gasR" << gasColor[0] << " dustR" << dustColor[0];
443
}
444

445

446
// Draw the Comet and all the related infos: name, circle etc... GZ: Taken from Planet.cpp 2013-11-05 and extended
UNCOV
447
void Comet::draw(StelCore* core, float maxMagLabels, const QFont& planetNameFont, const double eclipseFactor)
×
448
{
449
        Q_UNUSED(eclipseFactor)
UNCOV
450
        if (hidden)
×
451
                return;
×
452

UNCOV
453
        static SolarSystem *ss=GETSTELMODULE(SolarSystem);
×
UNCOV
454
        const float vMagnitude=getVMagnitude(core);
×
455

456
        // Exclude drawing if user set a hard limit magnitude.
UNCOV
457
        if (core->getSkyDrawer()->getFlagPlanetMagnitudeLimit() && (vMagnitude > core->getSkyDrawer()->getCustomPlanetMagnitudeLimit()))
×
UNCOV
458
                return;
×
459

UNCOV
460
        if (getEnglishName() == core->getCurrentLocation().planetName)
×
461
        { // Maybe even don't do that? E.g., draw tail while riding the comet? Decide later.
462
                return;
×
463
        }
464

465
        // This test seemed necessary for reasonable fps in case too many comet elements are loaded.
466
        // Problematic: Early-out here of course disables the wanted hint circles for dim comets.
467
        // The line makes hints for comets 5 magnitudes below sky limiting magnitude visible.
468
        // If comet is too faint to be seen, don't bother rendering. (Massive speedup if people have hundreds of comet elements!)
469
        if ((ss->getMarkerValue()==0.) && ((vMagnitude-5.0f) > core->getSkyDrawer()->getLimitMagnitude()) && !core->getCurrentLocation().planetName.contains("Observer", Qt::CaseInsensitive))
×
470
        {
UNCOV
471
                return;
×
472
        }
UNCOV
473
        if (!static_cast<KeplerOrbit*>(orbitPtr)->objectDateGoodEnoughForOrbits(core->getJDE())) return; // don't draw at all if out of useful date range. This allows having hundreds of comet elements.
×
474

UNCOV
475
        Mat4d mat = Mat4d::translation(eclipticPos+aberrationPush) * rotLocalToParent;
×
476
        // This removed totally the Planet shaking bug!!!
UNCOV
477
        StelProjector::ModelViewTranformP transfo = core->getHeliocentricEclipticModelViewTransform();
×
UNCOV
478
        transfo->combine(mat);
×
479

480
        // Compute the 2D position and check if in the screen
481
        const StelProjectorP prj = core->getProjection(transfo);
×
UNCOV
482
        const double screenRd = (getAngularRadius(core))*M_PI_180*static_cast<double>(prj->getPixelPerRadAtCenter());
×
483
        const double viewport_left = prj->getViewportPosX();
×
UNCOV
484
        const double viewport_bottom = prj->getViewportPosY();
×
485
        const bool projectionValid=prj->project(Vec3d(0.), screenPos);
×
UNCOV
486
        if (projectionValid
×
487
                && screenPos[1] > viewport_bottom - screenRd
×
UNCOV
488
                && screenPos[1] < viewport_bottom + prj->getViewportHeight()+screenRd
×
489
                && screenPos[0] > viewport_left - screenRd
×
490
                && screenPos[0] < viewport_left + prj->getViewportWidth() + screenRd)
×
491
        {
492
                // Draw the name, and the circle if it's not too close from the body it's turning around
493
                // this prevents name overlapping (ie for jupiter satellites)
494
                float ang_dist = 300.f*static_cast<float>(atan(getEclipticPos().norm()/getEquinoxEquatorialPos(core).norm())/core->getMovementMgr()->getCurrentFov());
×
495
                // if (ang_dist==0.f) ang_dist = 1.f; // if ang_dist == 0, the Planet is sun.. --> GZ: we can remove it.
496

497
                // by putting here, only draw orbit if Comet is visible for clarity
498
                drawOrbit(core);  // TODO - fade in here also...
×
499

500
                labelsFader = (flagLabels && ang_dist>0.25f && maxMagLabels>vMagnitude);
×
501

502
                { // encapsulate painter!
UNCOV
503
                        const StelProjectorP prjin = core->getProjection(StelCore::FrameJ2000);
×
UNCOV
504
                        StelPainter sPainter(prjin);
×
UNCOV
505
                        drawHints(core, sPainter, planetNameFont);
×
506
                        Vec3f color=Vec3f(0.25, 0.75, 1);
×
UNCOV
507
                        ss->drawAsteroidMarker(core, &sPainter, screenPos[0], screenPos[1], color); // This does not draw directly, but record an entry to be drawn in a batch.
×
UNCOV
508
                }
×
509

510
                draw3dModel(core,transfo,static_cast<float>(screenRd), 1.0);
×
511
        }
512
        else
UNCOV
513
                if (!projectionValid && prj.data()->getNameI18() == q_("Orthographic"))
×
UNCOV
514
                        return; // End prematurely. This excludes bad "ghost" comet tail on the wrong hemisphere in ortho projection! Maybe also Fisheye, but it's less problematic.
×
515
        else if (permanentDrawingOrbits) // A special case for demos
×
516
                        drawOrbit(core);
×
517

518

519
        // If comet is too faint to be seen, don't bother rendering. (Massive speedup if people have hundreds of comets!)
520
        // This test moved here so that hints are still drawn.
UNCOV
521
        if ((vMagnitude-5.0f) > core->getSkyDrawer()->getLimitMagnitude())
×
522
        {
UNCOV
523
                return;
×
524
        }
525

526
        // but tails should also be drawn if comet core is off-screen...
527
        if (tailActive && tailBright)
×
528
        {
UNCOV
529
                drawTail(core,transfo,true);  // gas tail
×
UNCOV
530
                drawTail(core,transfo,false); // dust tail
×
531
        }
532
        //Coma: this is just a fan disk tilted towards the observer;-)
533
        drawComa(core, transfo);
×
UNCOV
534
        return;
×
535
}
×
536

UNCOV
537
void Comet::drawTail(StelCore* core, StelProjector::ModelViewTranformP transfo, bool gas)
×
538
{        
539
        StelPainter sPainter(core->getProjection(transfo));
×
UNCOV
540
        sPainter.setBlending(true, GL_ONE, GL_ONE);
×
541

542
        tailTexture->bind();
×
543

UNCOV
544
        if (gas) {
×
545
                StelVertexArray vaGas(static_cast<const QVector<Vec3d> >(gastailVertexArr), StelVertexArray::Triangles,
×
546
                                      static_cast<const QVector<Vec2f> >(tailTexCoordArr), tailIndices, static_cast<const QVector<Vec3f> >(gastailColorArr));
×
547
                sPainter.drawStelVertexArray(vaGas, true);
×
548

549
        } else {
×
UNCOV
550
                StelVertexArray vaDust(static_cast<const QVector<Vec3d> >(dusttailVertexArr), StelVertexArray::Triangles,
×
551
                                      static_cast<const QVector<Vec2f> >(tailTexCoordArr), tailIndices, static_cast<const QVector<Vec3f> >(dusttailColorArr));
×
552
                sPainter.drawStelVertexArray(vaDust, true);
×
UNCOV
553
        }
×
554
        sPainter.setBlending(false);
×
UNCOV
555
}
×
556

557
void Comet::drawComa(StelCore* core, StelProjector::ModelViewTranformP transfo)
×
558
{
559
        // Find rotation matrix from 0/0/1 to viewdirection! crossproduct for axis (normal vector), dotproduct for angle.
UNCOV
560
        Vec3d eclposNrm=eclipticPos+aberrationPush - core->getObserverHeliocentricEclipticPos()  ; eclposNrm.normalize();
×
561
        Mat4d comarot=Mat4d::rotation(Vec3d(0.0, 0.0, 1.0)^(eclposNrm), std::acos(Vec3d(0.0, 0.0, 1.0).dot(eclposNrm)) );
×
562
        StelProjector::ModelViewTranformP transfo2 = transfo->clone();
×
563
        transfo2->combine(comarot);
×
564
        StelPainter sPainter(core->getProjection(transfo2));
×
565

566
        sPainter.setBlending(true, GL_ONE, GL_ONE);
×
567

UNCOV
568
        StelToneReproducer* eye = core->getToneReproducer();
×
569
        float lum = core->getSkyDrawer()->surfaceBrightnessToLuminance(getVMagnitudeWithExtinction(core)+11.0f); // How to calibrate?
×
570
        // Get the luminance scaled between 0 and 1
UNCOV
571
        const float aLum =eye->adaptLuminanceScaled(lum);
×
572
        const float magFactor=qBound(0.25f*intensityFovScale, aLum*intensityFovScale, 2.0f);
×
573
        comaTexture->bind();
×
574
        sPainter.setColor(0.3f*magFactor,0.7f*magFactor,magFactor);
×
575
        StelVertexArray vaComa(static_cast<const QVector<Vec3d> >(comaVertexArr), StelVertexArray::Triangles, static_cast<const QVector<Vec2f> >(comaTexCoordArr));
×
576
        sPainter.drawStelVertexArray(vaComa, true);
×
UNCOV
577
        sPainter.setBlending(false);
×
578
}
×
579

580
// Formula found at http://www.projectpluto.com/update7b.htm#comet_tail_formula
581
Vec2f Comet::getComaDiameterAndTailLengthAU() const
×
582
{
583
        const float r = static_cast<float>(getHeliocentricEclipticPos().norm());
×
584
        const float mhelio = absoluteMagnitude + slopeParameter * log10(r);
×
585
        const float Do = powf(10.0f, ((-0.0033f*mhelio - 0.07f) * mhelio + 3.25f));
×
586
        const float common = 1.0f - powf(10.0f, (-2.0f*r));
×
587
        const float D = Do * common * (1.0f - powf(10.0f, -r)) * (1000.0f*AU_KMf);
×
588
        const float Lo = powf(10.0f, ((-0.0075f*mhelio - 0.19f) * mhelio + 2.1f));
×
589
        const float L = Lo*(1.0f-powf(10.0f, -4.0f*r)) * common * (1e6f*AU_KMf);
×
590
        return Vec2f(D, L);
×
591
}
592

593
void Comet::computeComa(const float diameter)
×
594
{
595
        StelPainter::computeFanDisk(0.5f*diameter, 3, 3, comaVertexArr, comaTexCoordArr);
×
596
}
×
597

598
//! create parabola shell to represent a tail. Designed for slices=16, stacks=16, but should work with other sizes as well.
599
//! (Maybe slices must be an even number.)
600
// Parabola equation: z=x²/2p.
601
// xOffset for the dust tail, this may introduce a bend. Units are x per sqrt(z).
602
void Comet::computeParabola(const float parameter, const float radius, const float zshift,
×
603
                                                  QVector<Vec3d>& vertexArr, QVector<Vec2f>& texCoordArr,
604
                                                  QVector<unsigned short> &indices, const float xOffset)
605
{
606
        // keep the array and replace contents. However, using replace() is only slightly faster.
607
        if (vertexArr.length() < static_cast<int>(((COMET_TAIL_SLICES*COMET_TAIL_STACKS+1))))
×
608
                vertexArr.resize((COMET_TAIL_SLICES*COMET_TAIL_STACKS+1));
×
UNCOV
609
        if (createTailIndices) indices.clear();
×
UNCOV
610
        if (createTailTextureCoords) texCoordArr.clear();
×
611
        // The parabola has triangular faces with vertices on two circles that are rotated against each other. 
612
        float xa[2*COMET_TAIL_SLICES];
613
        float ya[2*COMET_TAIL_SLICES];
614
        float x, y, z;
615
        
616
        // fill xa, ya with sin/cosines. TBD: make more efficient with index mirroring etc.
UNCOV
617
        float da=M_PIf/COMET_TAIL_SLICES; // full circle/2slices
×
UNCOV
618
        for (unsigned short int i=0; i<2*COMET_TAIL_SLICES; ++i){
×
619
                xa[i]=-sin(i*da);
×
620
                ya[i]=cos(i*da);
×
621
        }
622
        
UNCOV
623
        vertexArr.replace(0, Vec3d(0.0, 0.0, static_cast<double>(zshift)));
×
UNCOV
624
        int vertexArrIndex=1;
×
UNCOV
625
        if (createTailTextureCoords) texCoordArr << Vec2f(0.5f, 0.5f);
×
626
        // define the indices lying on circles, starting at 1: odd rings have 1/slices+1/2slices, even-numbered rings straight 1/slices
627
        // inner ring#1
UNCOV
628
        for (unsigned short int ring=1; ring<=COMET_TAIL_STACKS; ++ring){
×
629
                z=ring*radius/COMET_TAIL_STACKS; z=z*z/(2*parameter) + zshift;
×
630
                const float xShift= xOffset*z*z;
×
631
                for (unsigned short int i=ring & 1; i<2*COMET_TAIL_SLICES; i+=2) { // i.e., ring1 has shifted vertices, ring2 has even ones.
×
632
                        x=xa[i]*radius*ring/COMET_TAIL_STACKS;
×
UNCOV
633
                        y=ya[i]*radius*ring/COMET_TAIL_STACKS;
×
UNCOV
634
                        vertexArr.replace(vertexArrIndex++, Vec3d(static_cast<double>(x+xShift), static_cast<double>(y), static_cast<double>(z)));
×
635
                        if (createTailTextureCoords) texCoordArr << Vec2f(0.5f+ 0.5f*x/radius, 0.5f+0.5f*y/radius);
×
636
                }
637
        }
638
        // now link the faces with indices.
UNCOV
639
        if (createTailIndices)
×
640
        {
641
                for (unsigned short i=1; i<COMET_TAIL_SLICES; ++i) indices << 0 << i << i+1;
×
642
                indices << 0 << COMET_TAIL_SLICES << 1; // close inner fan.
×
643
                // The other slices are a repeating pattern of 2 possibilities. Index @ring always is on the inner ring (slices-agon)
644
                for (unsigned short ring=1; ring<COMET_TAIL_STACKS; ring+=2) { // odd rings
×
645
                        const unsigned short int first=(ring-1)*COMET_TAIL_SLICES+1;
×
646
                        for (unsigned short int i=0; i<COMET_TAIL_SLICES-1; ++i){
×
647
                                indices << first+i << first+COMET_TAIL_SLICES+i << first+COMET_TAIL_SLICES+1+i;
×
UNCOV
648
                                indices << first+i << first+COMET_TAIL_SLICES+1+i << first+1+i;
×
649
                        }
650
                        // closing slice: mesh with other indices...
651
                        indices << ring*COMET_TAIL_SLICES << (ring+1)*COMET_TAIL_SLICES << ring*COMET_TAIL_SLICES+1;
×
UNCOV
652
                        indices << ring*COMET_TAIL_SLICES << ring*COMET_TAIL_SLICES+1 << first;
×
653
                }
654

UNCOV
655
                for (unsigned short int ring=2; ring<COMET_TAIL_STACKS; ring+=2) { // even rings: different sequence.
×
656
                        const unsigned short int first=(ring-1)*COMET_TAIL_SLICES+1;
×
657
                        for (unsigned short int i=0; i<COMET_TAIL_SLICES-1; ++i){
×
658
                                indices << first+i << first+COMET_TAIL_SLICES+i << first+1+i;
×
659
                                indices << first+1+i << first+COMET_TAIL_SLICES+i << first+COMET_TAIL_SLICES+1+i;
×
660
                        }
661
                        // closing slice: mesh with other indices...
UNCOV
662
                        indices << ring*COMET_TAIL_SLICES << (ring+1)*COMET_TAIL_SLICES << first;
×
663
                        indices << first << (ring+1)*COMET_TAIL_SLICES << ring*COMET_TAIL_SLICES+1;
×
664
                }
665
        }
UNCOV
666
        createTailIndices=false;
×
667
        createTailTextureCoords=false;
×
668
}
×
669

670
void Comet::setIAUDesignation(const QString& designation)
×
671
{
UNCOV
672
        static const QRegularExpression periodicDesignationPattern("^(\\d+)P/");
×
UNCOV
673
        static const QRegularExpression periodicCometPattern("^P/([\\w\\s]+)$");
×
674
        QRegularExpressionMatch matchPeriodicNumber = periodicDesignationPattern.match(englishName);
×
675
        QRegularExpressionMatch matchPeriodicComet  = periodicCometPattern.match(designation);
×
UNCOV
676
        if (matchPeriodicNumber.hasMatch() && matchPeriodicComet.hasMatch())
×
677
        {
678
                // combined designation for numbered periodic comets - 1P/1982 U1 or 146P/1984 W1
679
                iauDesignation = QString("%1P/%2").arg(matchPeriodicNumber.captured(1), matchPeriodicComet.captured(1));
×
680
        }
681
        else
682
                iauDesignation = designation;
×
UNCOV
683
}
×
684

685
void Comet::setExtraDesignations(QStringList codes)
×
686
{
687
        extraDesignations = codes;
×
688
        for (const auto& c : std::as_const(codes))
×
689
        {
UNCOV
690
                extraDesignationsHtml << renderDiscoveryDesignationHtml(c);
×
691
        }
UNCOV
692
}
×
693

694
QString Comet::renderDiscoveryDesignationHtml(const QString &plainTextName)
3✔
695
{
696
        static const QRegularExpression discoveryDesignationPattern("^(\\d{4}[a-z]{1})(\\d+)$");
3✔
697
        QRegularExpressionMatch match=discoveryDesignationPattern.match(plainTextName);
3✔
698
        if (match.hasMatch())
3✔
699
        {
700
                QString main = match.captured(1);
2✔
701
                QString suffix = match.captured(2);
2✔
702
                return (QString("%1<sub>%2</sub>").arg(main, suffix));
2✔
703
        }
2✔
704
        else
705
                return plainTextName;
1✔
706
}
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc