[Kstars-devel] KDE/kdeedu/kstars/kstars

Jason Harris kstars at 30doradus.org
Sun Jan 27 00:26:59 CET 2008


SVN commit 766933 by harris:

Cosmetic fixes for different projection schemes.  
The opaque ground polygon is now well-behaved for all 
projections.  Fixed a crash at low zoom when using 
Gnomonic projection, as reported by Mederic Boquien.

Gnomonic is a special case, because unlike the other 
projections, it has an infinite "sky horizon" 
boundary.  i.e., the locus of points that are 90 
degrees from the focus is projeced as a circle in 
other systems, but these points are at infinity in 
the Gnominic projection.

Also forward-porting the fix for Bug #133505 (crash 
condition in SkyMap::refract()).  Will backport these 
changes to the 4.0 branch as well.

CCMAIL: boquien at astro.umass.edu
CCMAIL: kstars-devel at kde.org



 M  +67 -26    skycomponents/horizoncomponent.cpp  
 M  +60 -7     skymap.cpp  


--- trunk/KDE/kdeedu/kstars/kstars/skycomponents/horizoncomponent.cpp #766932:766933
@@ -97,8 +97,12 @@
         psky.setBrush( Qt::NoBrush );
 
     double daz = 90.;
-    if ( Options::useAltAz() )
+    if ( Options::useAltAz() ) {
         daz = 0.5*Width*57.3/Options::zoomFactor(); //center to edge, in degrees
+        if ( Options::projection() == SkyMap::Orthographic ) {
+            daz = daz * 1.4;
+        }
+    }
     if ( daz > 90.0 ) daz = 90.0;
 
     double az1 = map->focus()->az()->Degrees() - daz;
@@ -153,14 +157,16 @@
         foreach ( SkyPoint *p, pointList() ) {
             if ( p->az()->Degrees() > az1 ) {
                 o = map->toScreen( p, false );
-                groundPoly << o;
+                if ( ! map->isPointNull( o ) ) {
+                    groundPoly << o;
 
-                //Set the anchor point if this point is onscreen
-                if ( o.x() < marginRight && o.y() > marginTop && o.y() < marginBot )
-                    pAnchor = p;
+                    //Set the anchor point if this point is onscreen
+                    if ( o.x() < marginRight && o.y() > marginTop && o.y() < marginBot )
+                        pAnchor = p;
 
-                if ( o.y() > 0. ) allGround = false;
-                if ( o.y() < Height ) allSky = false;
+                    if ( o.y() > 0. ) allGround = false;
+                    if ( o.y() < Height ) allSky = false;
+                }
             }
         }
         az1 = 0.0;
@@ -170,14 +176,16 @@
     foreach ( SkyPoint *p, pointList() ) {
         if ( p->az()->Degrees() > az1 && p->az()->Degrees() < az2 ) {
             o = map->toScreen( p, false );
-            groundPoly << o;
+            if ( ! map->isPointNull( o ) ) {
+                groundPoly << o;
 
-            //Set the anchor point if this point is onscreen
-            if ( o.x() < marginRight && o.y() > marginTop && o.y() < marginBot )
-                pAnchor = p;
+                //Set the anchor point if this point is onscreen
+                if ( o.x() < marginRight && o.y() > marginTop && o.y() < marginBot )
+                    pAnchor = p;
 
-            if ( o.y() > 0. ) allGround = false;
-            if ( o.y() < Height ) allSky = false;
+                if ( o.y() > 0. ) allGround = false;
+                if ( o.y() < Height ) allSky = false;
+            }
         } else if ( p->az()->Degrees() > az2 )
             break;
     }
@@ -188,16 +196,19 @@
         foreach ( SkyPoint *p, pointList() ) {
             if ( p->az()->Degrees() < az2 ) {
                 o = map->toScreen( p, false );
-                groundPoly << o;
+                if ( ! map->isPointNull( o ) ) {
+                    groundPoly << o;
 
-                //Set the anchor point if this point is onscreen
-                if ( o.x() < marginRight && o.y() > marginTop && o.y() < marginBot )
-                    pAnchor = p;
+                    //Set the anchor point if this point is onscreen
+                    if ( o.x() < marginRight && o.y() > marginTop && o.y() < marginBot )
+                        pAnchor = p;
 
-                if ( o.y() > 0. ) allGround = false;
-                if ( o.y() < Height ) allSky = false;
-            } else
+                    if ( o.y() > 0. ) allGround = false;
+                    if ( o.y() < Height ) allSky = false;
+                }
+            } else {
                 break;
+            }
         }
     }
 
@@ -243,19 +254,49 @@
         //complete the polygon by simply adding points along thebottom edge
         //of the screen.  If the zoomLevel is high (daz>75.0), then we add points
         //along the bottom edge of the sky circle.  The sky circle has
-        //a radius of 2*sin(pi/4)*ZoomFactor, and the endpoints of the current
+        //a radius determined by the projection scheme, and the endpoints of the current
         //groundPoly points lie 180 degrees apart along its circumference.
         //We determine the polar angles t1, t2 corresponding to these end points,
         //and then step along the circumference, adding points between them.
         //(In Horizontal coordinates, t1 and t2 are always 360 and 180, respectively).
     } else { //Horizontal coords
-        if ( daz < 75.0 ) { //can complete polygon by simply adding offscreen points
+
+        //In Gnomonic projection, or if sufficiently zoomed in, we can complete 
+        //the ground polygon by simply adding offscreen points
+        if ( daz < 25.0 || Options::projection() == SkyMap::Gnomonic ) { 
             groundPoly << QPointF( Width + 10., groundPoly.last().y() )
                 << QPointF( Width + 10., Height + 10. )
                 << QPointF( -10., Height + 10. )
                 << QPointF( -10., groundPoly.first().y() );
-        } else { //complete polygon along bottom of sky circle
-            double r0 = 2.0*map->scale()*sin(0.25*dms::PI);
+
+        //For other projections at low zoom, we complete the ground polygon by tracing 
+        //along the bottom of sky's horizon circle (i.e., the locus of points that are 
+        //90 degrees from the focus point)
+        } else { 
+            //r0, the radius of the sky circle is determined by the projection scheme:
+            double r0 = map->scale()*Options::zoomFactor();
+            switch ( Options::projection() ) {
+            case SkyMap::Lambert:
+                //r0 * 2 * sin(PI/4) = 1.41421356
+                r0 = r0*1.41421356;
+                break;
+            case SkyMap::AzimuthalEquidistant:
+                //r0*PI/2 = 1.57079633
+                r0 = r0*1.57079633;
+                break;
+            case SkyMap::Orthographic:
+                //r0*sin(PI/2) = 1.0
+                break;
+            case SkyMap::Stereographic:
+                //r0*2*tan(PI/4) = 2.0
+                r0 = 2.0*r0;
+                break;
+            default:
+                kWarning() << i18n("Unrecognized coordinate projection: ") << Options::projection() ;
+                //default to Orthographic
+                break;
+            }
+
             double t1 = 360.;
             double t2 = 180.;
 
@@ -275,8 +316,8 @@
                 dms a( t );
                 double sa(0.), ca(0.);
                 a.SinCos( sa, ca );
-                float xx = 0.5*Width  + r0*Options::zoomFactor()*ca;
-                float yy = 0.5*Height - r0*Options::zoomFactor()*sa;
+                float xx = 0.5*Width  + r0*ca;
+                float yy = 0.5*Height - r0*sa;
 
                 groundPoly << QPoint( int(xx), int(yy) );
             }
--- trunk/KDE/kdeedu/kstars/kstars/skymap.cpp #766932:766933
@@ -935,6 +935,22 @@
     cosY  = cos(Y);
 	#endif
 
+    // Reference for these map projections: Wolfram MathWorld
+    // Lambert Azimuthal Equal-Area:
+    // http://mathworld.wolfram.com/LambertAzimuthalEqual-AreaProjection.html
+    // 
+    // Azimuthal Equidistant:
+    // http://mathworld.wolfram.com/AzimuthalEquidistantProjection.html
+    //
+    // Orthographic:
+    // http://mathworld.wolfram.com/OrthographicProjection.html
+    //
+    // Stereographic:
+    // http://mathworld.wolfram.com/StereographicProjection.html
+    //
+    // Gnomonic:
+    // http://mathworld.wolfram.com/GnomonicProjection.html
+
     //c is the cosine of the angular distance from the center
     double c = sinY0*sinY + cosY0*cosY*cosdX;
 
@@ -942,9 +958,12 @@
         *onVisibleHemisphere = true;
     }
 
-    if ( c < 0.0 ) {
-        //Object is on "back side" of the celestial sphere;
-        //Set null coordinates and return
+    //If c is less than 0.0, then the "field angle" (angular distance from the focus) 
+    //is more than 90 degrees.  This is on the "back side" of the celestial sphere 
+    //and should not be drawn.
+    //The Gnomonic projection has an infinite sky horizon, so don't allow the field
+    //angle to approach 90 degrees in thi scase (cut it off at c=0.2).
+    if ( c < 0.0 || Options::projection()==Gnomonic && c < 0.2 ) {
         if (onVisibleHemisphere == NULL) {
             p.setX( -10000000. );
             p.setY( -10000000. );
@@ -1399,6 +1418,12 @@
     if ( alt->Degrees()<x1 ) index2 = index - 1;
     if ( index2 < 0 ) index2 = index + 1;
     if ( index2 > 183 ) index2 = index - 1;
+
+    //Failsafe: if indices are out of range, return the input altitude
+    if ( index < 0 || index > 183 || index2 < 0 || index2 > 183 ) {
+        return dms( alt->Degrees() );
+    }
+
     double x2 = 0.5*float(index2) - 1.75;
     double dx = (alt->Degrees() - x1)/(x2 - x1);
 
@@ -1483,16 +1508,44 @@
     }
 }
 
-bool SkyMap::unusablePoint ( const QPointF &p )
+bool SkyMap::unusablePoint( const QPointF &p )
 {
+    double r0 = 1.0;
+    //r0 is the angular size of the sky horizon, in radians
+    //See HorizonComponent::draw() for documentation of these values
+    switch ( Options::projection() ) {
+    case Lambert:
+        r0 = 1.41421356; break;
+    case AzimuthalEquidistant:
+        r0 = 1.57079633; break;
+    case Stereographic:
+        r0 = 2.0; break;
+    case Gnomonic:
+        r0 = 6.28318531; break; //Gnomonic has an infinite horizon; this is 2*PI
+    case Orthographic:
+    default:
+        break;
+    }
+
+    //If the zoom is high enough, all points are usable
+    //The center-to-corner distance, in radians
+    double r = 0.5*1.41421356*width() / Options::zoomFactor();
+    if ( r < r0 ) {
+        return false;
+    }
+
+    //At low zoom, we have to determine whether the point is beyond the sky horizon
     //Convert pixel position to x and y offsets in radians
     double dx = ( 0.5*width()  - p.x() )/Options::zoomFactor();
     double dy = ( 0.5*height() - p.y() )/Options::zoomFactor();
+    double rsq = ( dx*dx + dy*dy );
+    r0 = r0*r0;
 
-    if (dx >= 1.41 || dx <= -1.41 || dy >= 1.41 || dy <= -1.41)
-        return true;
-    else
+    if (rsq < r0) {
         return false;
+    }
+
+    return true;
 }
 
 void SkyMap::setZoomMouseCursor()


More information about the Kstars-devel mailing list