// returns true if visible // Assumes that we are in local frame void MeteorStream::draw(const StelCore* core, StelPainter& sPainter) { if(!alive) return; Vec3d spos = position; Vec3d epos = posTrain; // convert to equ spos.transfo4d(viewMatrix); epos.transfo4d(viewMatrix); // convert to local and correct for earth radius //[since equ and local coordinates in stellarium use same 0 point!] spos = core->j2000ToAltAz(spos); epos = core->j2000ToAltAz(epos); spos[2] -= EARTH_RADIUS; epos[2] -= EARTH_RADIUS; // 1216 is to scale down under 1 for desktop version spos/=1216; epos/=1216; // connect this point with last drawn point double tmag = mag*distMultiplier; QVector<Vec4f> colorArray; QVector<Vec3d> vertexArray; // last point - dark colorArray.push_back(Vec4f(0,0,0,0)); vertexArray.push_back(epos); // compute intermediate points to curve along projection distortions int segments = 10; for (int i=1; i<segments; i++) { Vec3d posi = posInternal; posi[2] = posTrain[2] + i*(position[2] - posTrain[2])/segments; posi.transfo4d(viewMatrix); posi = core->j2000ToAltAz(posi); posi[2] -= EARTH_RADIUS; posi/=1216; colorArray.push_back(Vec4f(1,1,1,i*tmag/segments)); vertexArray.push_back(posi); } // first point - light colorArray.push_back(Vec4f(1,1,1,tmag)); vertexArray.push_back(spos); sPainter.setColorPointer(4, GL_FLOAT, colorArray.constData()); sPainter.setVertexPointer(3, GL_DOUBLE, vertexArray.constData()); sPainter.enableClientStates(true, false, true); sPainter.drawFromArray(StelPainter::LineStrip, vertexArray.size(), 0, true); sPainter.enableClientStates(false); }
void Meteor::init(const float& radiantAlpha, const float& radiantDelta, const float& speed, const QList<ColorPair> colors) { // meteor velocity in km/s m_speed = speed; // find the radiant in horizontal coordinates Vec3d radiantAltAz; StelUtils::spheToRect(radiantAlpha, radiantDelta, radiantAltAz); radiantAltAz = m_core->j2000ToAltAz(radiantAltAz); float radiantAlt, radiantAz; // S is zero, E is 90 degrees (SDSS) StelUtils::rectToSphe(&radiantAz, &radiantAlt, radiantAltAz); // meteors won't be visible if radiant is below 0degrees if (radiantAlt < 0.f) { return; } // define the radiant coordinate system // rotation matrix to align z axis with radiant m_matAltAzToRadiant = Mat4d::zrotation(radiantAz) * Mat4d::yrotation(M_PI_2 - radiantAlt); // select a random initial meteor altitude in the horizontal system [MIN_ALTITUDE, MAX_ALTITUDE] float initialAlt = MIN_ALTITUDE + (MAX_ALTITUDE - MIN_ALTITUDE) * ((float) qrand() / ((float) RAND_MAX + 1)); // calculates the max z-coordinate for the currrent radiant float maxZ = meteorZ(M_PI_2 - radiantAlt, initialAlt); // meteor trajectory // select a random xy position in polar coordinates (radiant system) float xyDist = maxZ * ((double) qrand() / ((double) RAND_MAX + 1)); // [0, maxZ] float theta = 2 * M_PI * ((double) qrand() / ((double) RAND_MAX + 1)); // [0, 2pi] // initial meteor coordinates (radiant system) m_position[0] = xyDist * qCos(theta); m_position[1] = xyDist * qSin(theta); m_position[2] = maxZ; m_posTrain = m_position; // store the initial z-component (radiant system) m_initialZ = m_position[2]; // find the initial meteor coordinates in the horizontal system Vec3d positionAltAz = m_position; positionAltAz.transfo4d(m_matAltAzToRadiant); // find the angle from horizon to meteor float meteorAlt = qAsin(positionAltAz[2] / positionAltAz.length()); // this meteor should not be visible if it is above the maximum altitude // or if it's below the horizon! if (positionAltAz[2] > MAX_ALTITUDE || meteorAlt <= 0.f) { return; } // determine the final z-component and the min distance between meteor and observer if (radiantAlt < 0.0262f) // (<1.5 degrees) earth grazing meteor ? { // earth-grazers are rare! // introduce a probabilistic factor just to make them a bit harder to occur float prob = ((float) qrand() / ((float) RAND_MAX + 1)); if (prob > 0.3f) { return; } // limit lifetime to 12sec m_finalZ = -m_position[2]; m_finalZ = qMax(m_position[2] - m_speed * 12.f, (double) m_finalZ); m_minDist = xyDist; } else { // limit lifetime to 12sec m_finalZ = meteorZ(M_PI_2 - meteorAlt, MIN_ALTITUDE); m_finalZ = qMax(m_position[2] - m_speed * 12.f, (double) m_finalZ); m_minDist = qSqrt(m_finalZ * m_finalZ + xyDist * xyDist); } // a meteor cannot hit the observer! if (m_minDist < MIN_ALTITUDE) { return; } // select random magnitude [-3; 4.5] float Mag = (float) qrand() / ((float) RAND_MAX + 1) * 7.5f - 3.f; // compute RMag and CMag RCMag rcMag; m_core->getSkyDrawer()->computeRCMag(Mag, &rcMag); m_absMag = rcMag.radius <= 1.2f ? 0.f : rcMag.luminance; if (m_absMag == 0.f) { return; } // most visible meteors are under about 184km distant // scale max mag down if outside this range float scale = qPow(184.0 / m_minDist, 2); m_absMag *= qMin(scale, 1.0f); // build the color vector buildColorVectors(colors); m_alive = true; }
//! Draw the sky grid in the current frame void SkyGrid::draw(const StelCore* core) const { const StelProjectorP prj = core->getProjection(frameType, frameType!=StelCore::FrameAltAz ? StelCore::RefractionAuto : StelCore::RefractionOff); if (!fader.getInterstate()) return; bool withDecimalDegree = dynamic_cast<StelGui*>(StelApp::getInstance().getGui())->getFlagShowDecimalDegrees(); // Look for all meridians and parallels intersecting with the disk bounding the viewport // Check whether the pole are in the viewport bool northPoleInViewport = false; bool southPoleInViewport = false; Vec3f win; if (prj->project(Vec3f(0,0,1), win) && prj->checkInViewport(win)) northPoleInViewport = true; if (prj->project(Vec3f(0,0,-1), win) && prj->checkInViewport(win)) southPoleInViewport = true; // Get the longitude and latitude resolution at the center of the viewport Vec3d centerV; prj->unProject(prj->getViewportPosX()+prj->getViewportWidth()/2, prj->getViewportPosY()+prj->getViewportHeight()/2+1, centerV); double lon2, lat2; StelUtils::rectToSphe(&lon2, &lat2, centerV); const double gridStepParallelRad = M_PI/180.*getClosestResolutionDMS(prj->getPixelPerRadAtCenter()); double gridStepMeridianRad; if (northPoleInViewport || southPoleInViewport) gridStepMeridianRad = (frameType==StelCore::FrameAltAz || frameType==StelCore::FrameGalactic) ? M_PI/180.* 10. : M_PI/180.* 15.; else { const double closetResLon = (frameType==StelCore::FrameAltAz || frameType==StelCore::FrameGalactic) ? getClosestResolutionDMS(prj->getPixelPerRadAtCenter()*std::cos(lat2)) : getClosestResolutionHMS(prj->getPixelPerRadAtCenter()*std::cos(lat2)); gridStepMeridianRad = M_PI/180.* ((northPoleInViewport || southPoleInViewport) ? 15. : closetResLon); } // Get the bounding halfspace const SphericalCap& viewPortSphericalCap = prj->getBoundingCap(); // Compute the first grid starting point. This point is close to the center of the screen // and lays at the intersection of a meridien and a parallel lon2 = gridStepMeridianRad*((int)(lon2/gridStepMeridianRad+0.5)); lat2 = gridStepParallelRad*((int)(lat2/gridStepParallelRad+0.5)); Vec3d firstPoint; StelUtils::spheToRect(lon2, lat2, firstPoint); firstPoint.normalize(); // Q_ASSERT(viewPortSphericalCap.contains(firstPoint)); // Initialize a painter and set openGL state StelPainter sPainter(prj); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Normal transparency mode Vec4f textColor(color[0], color[1], color[2], 0); sPainter.setColor(color[0],color[1],color[2], fader.getInterstate()); textColor*=2; textColor[3]=fader.getInterstate(); sPainter.setFont(font); ViewportEdgeIntersectCallbackData userData(&sPainter); userData.textColor = textColor; userData.frameType = frameType; ///////////////////////////////////////////////// // Draw all the meridians (great circles) SphericalCap meridianSphericalCap(Vec3d(1,0,0), 0); Mat4d rotLon = Mat4d::zrotation(gridStepMeridianRad); Vec3d fpt = firstPoint; Vec3d p1, p2; int maxNbIter = (int)(M_PI/gridStepMeridianRad); int i; for (i=0; i<maxNbIter; ++i) { StelUtils::rectToSphe(&lon2, &lat2, fpt); userData.raAngle = lon2; meridianSphericalCap.n = fpt^Vec3d(0,0,1); meridianSphericalCap.n.normalize(); if (!SphericalCap::intersectionPoints(viewPortSphericalCap, meridianSphericalCap, p1, p2)) { if (viewPortSphericalCap.d<meridianSphericalCap.d && viewPortSphericalCap.contains(meridianSphericalCap.n)) { // The meridian is fully included in the viewport, draw it in 3 sub-arcs to avoid length > 180. const Mat4d& rotLon120 = Mat4d::rotation(meridianSphericalCap.n, 120.*M_PI/180.); Vec3d rotFpt=fpt; rotFpt.transfo4d(rotLon120); Vec3d rotFpt2=rotFpt; rotFpt2.transfo4d(rotLon120); sPainter.drawGreatCircleArc(fpt, rotFpt, NULL, viewportEdgeIntersectCallback, &userData); sPainter.drawGreatCircleArc(rotFpt, rotFpt2, NULL, viewportEdgeIntersectCallback, &userData); sPainter.drawGreatCircleArc(rotFpt2, fpt, NULL, viewportEdgeIntersectCallback, &userData); fpt.transfo4d(rotLon); continue; } else break; } Vec3d middlePoint = p1+p2; middlePoint.normalize(); if (!viewPortSphericalCap.contains(middlePoint)) middlePoint*=-1.; // Draw the arc in 2 sub-arcs to avoid lengths > 180 deg sPainter.drawGreatCircleArc(p1, middlePoint, NULL, viewportEdgeIntersectCallback, &userData); sPainter.drawGreatCircleArc(p2, middlePoint, NULL, viewportEdgeIntersectCallback, &userData); fpt.transfo4d(rotLon); } if (i!=maxNbIter) { rotLon = Mat4d::zrotation(-gridStepMeridianRad); fpt = firstPoint; fpt.transfo4d(rotLon); for (int j=0; j<maxNbIter-i; ++j) { StelUtils::rectToSphe(&lon2, &lat2, fpt); userData.raAngle = lon2; meridianSphericalCap.n = fpt^Vec3d(0,0,1); meridianSphericalCap.n.normalize(); if (!SphericalCap::intersectionPoints(viewPortSphericalCap, meridianSphericalCap, p1, p2)) break; Vec3d middlePoint = p1+p2; middlePoint.normalize(); if (!viewPortSphericalCap.contains(middlePoint)) middlePoint*=-1; sPainter.drawGreatCircleArc(p1, middlePoint, NULL, viewportEdgeIntersectCallback, &userData); sPainter.drawGreatCircleArc(p2, middlePoint, NULL, viewportEdgeIntersectCallback, &userData); fpt.transfo4d(rotLon); } } ///////////////////////////////////////////////// // Draw all the parallels (small circles) SphericalCap parallelSphericalCap(Vec3d(0,0,1), 0); rotLon = Mat4d::rotation(firstPoint^Vec3d(0,0,1), gridStepParallelRad); fpt = firstPoint; maxNbIter = (int)(M_PI/gridStepParallelRad)-1; for (i=0; i<maxNbIter; ++i) { StelUtils::rectToSphe(&lon2, &lat2, fpt); if (withDecimalDegree) userData.text = StelUtils::radToDecDegStr(lat2); else userData.text = StelUtils::radToDmsStrAdapt(lat2); parallelSphericalCap.d = fpt[2]; if (parallelSphericalCap.d>0.9999999) break; const Vec3d rotCenter(0,0,parallelSphericalCap.d); if (!SphericalCap::intersectionPoints(viewPortSphericalCap, parallelSphericalCap, p1, p2)) { if ((viewPortSphericalCap.d<parallelSphericalCap.d && viewPortSphericalCap.contains(parallelSphericalCap.n)) || (viewPortSphericalCap.d<-parallelSphericalCap.d && viewPortSphericalCap.contains(-parallelSphericalCap.n))) { // The parallel is fully included in the viewport, draw it in 3 sub-arcs to avoid lengths >= 180 deg static const Mat4d rotLon120 = Mat4d::zrotation(120.*M_PI/180.); Vec3d rotFpt=fpt; rotFpt.transfo4d(rotLon120); Vec3d rotFpt2=rotFpt; rotFpt2.transfo4d(rotLon120); sPainter.drawSmallCircleArc(fpt, rotFpt, rotCenter, viewportEdgeIntersectCallback, &userData); sPainter.drawSmallCircleArc(rotFpt, rotFpt2, rotCenter, viewportEdgeIntersectCallback, &userData); sPainter.drawSmallCircleArc(rotFpt2, fpt, rotCenter, viewportEdgeIntersectCallback, &userData); fpt.transfo4d(rotLon); continue; } else break; } // Draw the arc in 2 sub-arcs to avoid lengths > 180 deg Vec3d middlePoint = p1-rotCenter+p2-rotCenter; middlePoint.normalize(); middlePoint*=(p1-rotCenter).length(); middlePoint+=rotCenter; if (!viewPortSphericalCap.contains(middlePoint)) { middlePoint-=rotCenter; middlePoint*=-1.; middlePoint+=rotCenter; } sPainter.drawSmallCircleArc(p1, middlePoint, rotCenter, viewportEdgeIntersectCallback, &userData); sPainter.drawSmallCircleArc(p2, middlePoint, rotCenter, viewportEdgeIntersectCallback, &userData); fpt.transfo4d(rotLon); } if (i!=maxNbIter) { rotLon = Mat4d::rotation(firstPoint^Vec3d(0,0,1), -gridStepParallelRad); fpt = firstPoint; fpt.transfo4d(rotLon); for (int j=0; j<maxNbIter-i; ++j) { StelUtils::rectToSphe(&lon2, &lat2, fpt); if (withDecimalDegree) userData.text = StelUtils::radToDecDegStr(lat2); else userData.text = StelUtils::radToDmsStrAdapt(lat2); parallelSphericalCap.d = fpt[2]; const Vec3d rotCenter(0,0,parallelSphericalCap.d); if (!SphericalCap::intersectionPoints(viewPortSphericalCap, parallelSphericalCap, p1, p2)) { if ((viewPortSphericalCap.d<parallelSphericalCap.d && viewPortSphericalCap.contains(parallelSphericalCap.n)) || (viewPortSphericalCap.d<-parallelSphericalCap.d && viewPortSphericalCap.contains(-parallelSphericalCap.n))) { // The parallel is fully included in the viewport, draw it in 3 sub-arcs to avoid lengths >= 180 deg static const Mat4d rotLon120 = Mat4d::zrotation(120.*M_PI/180.); Vec3d rotFpt=fpt; rotFpt.transfo4d(rotLon120); Vec3d rotFpt2=rotFpt; rotFpt2.transfo4d(rotLon120); sPainter.drawSmallCircleArc(fpt, rotFpt, rotCenter, viewportEdgeIntersectCallback, &userData); sPainter.drawSmallCircleArc(rotFpt, rotFpt2, rotCenter, viewportEdgeIntersectCallback, &userData); sPainter.drawSmallCircleArc(rotFpt2, fpt, rotCenter, viewportEdgeIntersectCallback, &userData); fpt.transfo4d(rotLon); continue; } else break; } // Draw the arc in 2 sub-arcs to avoid lengths > 180 deg Vec3d middlePoint = p1-rotCenter+p2-rotCenter; middlePoint.normalize(); middlePoint*=(p1-rotCenter).length(); middlePoint+=rotCenter; if (!viewPortSphericalCap.contains(middlePoint)) { middlePoint-=rotCenter; middlePoint*=-1.; middlePoint+=rotCenter; } sPainter.drawSmallCircleArc(p1, middlePoint, rotCenter, viewportEdgeIntersectCallback, &userData); sPainter.drawSmallCircleArc(p2, middlePoint, rotCenter, viewportEdgeIntersectCallback, &userData); fpt.transfo4d(rotLon); } } }
//Bennett's formula is not a strict inverse of Saemundsson's. There is a notable discrepancy (alt!=backward(forward(alt))) for // geometric altitudes <-.3deg. This is not a problem in real life, but if a user switches off landscape, click-identify may pose a problem. // Below this altitude, we therefore use a polynomial fit that should represent a close inverse of Saemundsson. void Refraction::backward(Vec3d& altAzPos) const { altAzPos.transfo4d(invertPostTransfoMat); innerRefractionBackward(altAzPos); altAzPos.transfo4d(invertPreTransfoMat); }
void Refraction::forward(Vec3d& altAzPos) const { altAzPos.transfo4d(preTransfoMat); innerRefractionForward(altAzPos); altAzPos.transfo4d(postTransfoMat); }
// returns true if visible bool Meteor::draw(Projector *proj, Navigator* nav) { if (!alive) return(0); Vec3d start, end; Vec3d spos = position; Vec3d epos = pos_train; // convert to equ spos.transfo4d(mmat); epos.transfo4d(mmat); // convert to local and correct for earth radius [since equ and local coordinates use same 0 point!] spos = nav->earth_equ_to_local( spos ); epos = nav->earth_equ_to_local( epos ); spos[2] -= EARTH_RADIUS; epos[2] -= EARTH_RADIUS; int t1 = proj->project_local_check(spos/1216, start); // 1216 is to scale down under 1 for desktop version int t2 = proj->project_local_check(epos/1216, end); // don't draw if not visible (but may come into view) if ( t1 + t2 == 0 ) return 1; // printf("[%f %f %f] (%d, %d) (%d, %d)\n", position[0], position[1], position[2], (int)start[0], (int)start[1], (int)end[0], (int)end[1]); if ( train ) { // connect this point with last drawn point double tmag = mag*dist_multiplier; // compute an intermediate point so can curve slightly along projection distortions Vec3d intpos; Vec3d posi = pos_internal; posi[2] = position[2] + (pos_train[2] - position[2])/2; posi.transfo4d(mmat); posi = nav->earth_equ_to_local( posi ); posi[2] -= EARTH_RADIUS; proj->project_local(posi/1216, intpos); // draw dark to light glBegin(GL_LINE_STRIP); glColor4f(0,0,0,0); glVertex3f(end[0],end[1],0); glColor4f(1,1,1,tmag/2); glVertex3f(intpos[0],intpos[1],0); glColor4f(1,1,1,tmag); glVertex3f(start[0],start[1],0); glEnd(); } else { //glPointSize(1); // glpoint is not portable... glBegin(GL_POINTS); glVertex3f(start[0],start[1],0); glEnd(); } /* // show radiant Vec3d radiant = Vec3d(0,0,0.5f); radiant.transfo4d(mmat); if( projection->project_earth_equ(radiant, start) ) { glColor3f(1,0,1); glBegin(GL_LINES); glVertex3f(start[0]-10,start[1],0); glVertex3f(start[0]+10,start[1],0); glEnd(); glBegin(GL_LINES); glVertex3f(start[0],start[1]-10,0); glVertex3f(start[0],start[1]+10,0); glEnd(); } */ train = 1; return(1); }
// returns true if visible // Assumes that we are in local frame void Meteor::draw(const StelCore* core, StelPainter& sPainter) { if (!alive) return; const StelProjectorP proj = sPainter.getProjector(); Vec3d spos = position; Vec3d epos = posTrain; // convert to equ spos.transfo4d(mmat); epos.transfo4d(mmat); // convert to local and correct for earth radius [since equ and local coordinates in stellarium use same 0 point!] spos = core->equinoxEquToAltAz( spos ); epos = core->equinoxEquToAltAz( epos ); spos[2] -= EARTH_RADIUS; epos[2] -= EARTH_RADIUS; // 1216 is to scale down under 1 for desktop version spos/=1216; epos/=1216; // qDebug("[%f %f %f] (%d, %d) (%d, %d)\n", position[0], position[1], position[2], (int)start[0], (int)start[1], (int)end[0], (int)end[1]); if (train) { // connect this point with last drawn point double tmag = mag*distMultiplier; // compute an intermediate point so can curve slightly along projection distortions Vec3d posi = posInternal; posi[2] = position[2] + (posTrain[2] - position[2])/2; posi.transfo4d(mmat); posi = core->equinoxEquToAltAz( posi ); posi[2] -= EARTH_RADIUS; posi/=1216; // draw dark to light Vec4f colorArray[3]; colorArray[0].set(0,0,0,0); colorArray[1].set(1,1,1,tmag*0.5); colorArray[2].set(1,1,1,tmag); Vec3d vertexArray[3]; vertexArray[0]=epos; vertexArray[1]=posi; vertexArray[2]=spos; sPainter.setColorPointer(4, GL_FLOAT, colorArray); sPainter.setVertexPointer(3, GL_DOUBLE, vertexArray); // TODO the crash doesn't appear when the last true is set to false sPainter.enableClientStates(true, false, true); sPainter.drawFromArray(StelPainter::LineStrip, 3, 0, true); sPainter.enableClientStates(false); } else { sPainter.setPointSize(1.f); Vec3d start; proj->project(spos, start); sPainter.drawPoint2d(start[0],start[1]); } train = 1; }