• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

kstars

skymapdraw.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002                           skymapdraw.cpp  -  K Desktop Planetarium
00003                              -------------------
00004     begin                : Sun Mar 2 2003
00005     copyright            : (C) 2001 by Jason Harris
00006     email                : jharris@30doradus.org
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 //This file contains drawing functions SkyMap class.
00019 
00020 #include <stdlib.h> // abs
00021 #include <math.h> //log10()
00022 #include <iostream>
00023 
00024 #include <qpaintdevicemetrics.h>
00025 #include <qpainter.h>
00026 
00027 #include "skymap.h"
00028 #include "Options.h"
00029 #include "kstars.h"
00030 #include "kstarsdata.h"
00031 #include "ksnumbers.h"
00032 #include "skyobject.h"
00033 #include "deepskyobject.h"
00034 #include "starobject.h"
00035 #include "ksplanetbase.h"
00036 #include "ksasteroid.h"
00037 #include "kscomet.h"
00038 #include "ksmoon.h"
00039 #include "jupitermoons.h"
00040 #include "infoboxes.h"
00041 #include "simclock.h"
00042 #include "csegment.h"
00043 #include "customcatalog.h"
00044 #include "devicemanager.h"
00045 #include "indimenu.h"
00046 #include "indiproperty.h"
00047 #include "indielement.h"
00048 #include "indidevice.h"
00049 
00050 void SkyMap::drawOverlays( QPixmap *pm ) {
00051     if ( ksw ) { //only if we are drawing in the GUI window
00052         QPainter p;
00053         p.begin( pm );
00054 
00055         showFocusCoords( true );
00056         drawBoxes( p );
00057 
00058         //draw FOV symbol
00059         ksw->data()->fovSymbol.draw( p, (float)(Options::fOVSize() * Options::zoomFactor()/57.3/60.0) );
00060         drawTelescopeSymbols( p );
00061         drawObservingList( p );
00062         drawZoomBox( p );
00063         if ( transientObject() ) drawTransientLabel( p );
00064         if (isAngleMode()) {
00065             updateAngleRuler();
00066             drawAngleRuler( p );
00067         }
00068     }
00069 }
00070 
00071 void SkyMap::drawAngleRuler( QPainter &p ) {
00072     p.setPen( QPen( data->colorScheme()->colorNamed( "AngularRuler" ), 1, DotLine ) );
00073     p.drawLine( beginRulerPoint, endRulerPoint );
00074 }
00075 
00076 void SkyMap::drawZoomBox( QPainter &p ) {
00077     //draw the manual zoom-box, if it exists
00078     if ( ZoomRect.isValid() ) {
00079         p.setPen( QPen( "white", 1, DotLine ) );
00080         p.drawRect( ZoomRect.x(), ZoomRect.y(), ZoomRect.width(), ZoomRect.height() );
00081     }
00082 }
00083 
00084 void SkyMap::drawTransientLabel( QPainter &p ) {
00085     if ( transientObject() ) {
00086         p.setPen( TransientColor );
00087 
00088         if ( checkVisibility( transientObject(), fov(), XRange ) ) {
00089             QPoint o = getXY( transientObject(), Options::useAltAz(), Options::useRefraction(), 1.0 );
00090             if ( o.x() >= 0 && o.x() <= width() && o.y() >= 0 && o.y() <= height() ) {
00091                 drawNameLabel( p, transientObject(), o.x(), o.y(), 1.0 );
00092             }
00093         }
00094     }
00095 }
00096 
00097 void SkyMap::drawBoxes( QPainter &p ) {
00098     if ( ksw ) { //only if we are drawing in the GUI window
00099         ksw->infoBoxes()->drawBoxes( p,
00100                 data->colorScheme()->colorNamed( "BoxTextColor" ),
00101                 data->colorScheme()->colorNamed( "BoxGrabColor" ),
00102                 data->colorScheme()->colorNamed( "BoxBGColor" ), Options::boxBGMode() );
00103     }
00104 }
00105 
00106 void SkyMap::drawObservingList( QPainter &psky, double scale ) {
00107     psky.setPen( QPen( QColor( data->colorScheme()->colorNamed( "ObsListColor" ) ), 1 ) );
00108 
00109     if ( ksw && ksw->observingList()->count() ) {
00110         for ( SkyObject* obj = ksw->observingList()->first(); obj; obj = ksw->observingList()->next() ) {
00111             
00112             if ( checkVisibility( obj, fov(), XRange ) ) {
00113                 QPoint o = getXY( obj, Options::useAltAz(), Options::useRefraction() );
00114 
00115                 // label object if it is currently on screen
00116                 if (o.x() >= 0 && o.x() <= width() && o.y() >=0 && o.y() <= height() ) {
00117                     if ( Options::obsListSymbol() ) {
00118                         int size = int(20*scale);
00119                         int x1 = o.x() - size/2;
00120                         int y1 = o.y() - size/2;
00121                         psky.drawArc( x1, y1, size, size, -60*16, 120*16 );
00122                         psky.drawArc( x1, y1, size, size, 120*16, 120*16 );
00123                     }
00124                     if ( Options::obsListText() ) {
00125                         drawNameLabel( psky, obj, o.x(), o.y(), scale );
00126                     }
00127                 }
00128             }
00129         }
00130     }
00131 }
00132 
00133 void SkyMap::drawTelescopeSymbols(QPainter &psky) {
00134 
00135     if ( ksw ) { //ksw doesn't exist in non-GUI mode!
00136         INDI_P *eqNum;
00137         INDI_P *portConnect;
00138         INDI_E *lp;
00139         INDIMenu *devMenu = ksw->getINDIMenu();
00140         bool useJ2000 (false), useAltAz(false);
00141         SkyPoint indi_sp;
00142 
00143         if (!Options::indiCrosshairs() || devMenu == NULL)
00144             return;
00145 
00146         psky.setPen( QPen( QColor( data->colorScheme()->colorNamed("TargetColor" ) ) ) );
00147         psky.setBrush( NoBrush );
00148         int pxperdegree = int(Options::zoomFactor()/57.3);
00149 
00150         //fprintf(stderr, "in draw telescope function with mgrsize of %d\n", devMenu->mgr.size());
00151         for (uint i=0; i < devMenu->mgr.count(); i++)
00152         {
00153             for (uint j=0; j < devMenu->mgr.at(i)->indi_dev.count(); j++)
00154             {
00155                 useAltAz = false;
00156                 useJ2000 = false;
00157 
00158                 // make sure the dev is on first
00159                 if (devMenu->mgr.at(i)->indi_dev.at(j)->isOn())
00160                 {
00161                         portConnect = devMenu->mgr.at(i)->indi_dev.at(j)->findProp("CONNECTION");
00162 
00163                     if (!portConnect)
00164                      return;
00165 
00166                      if (portConnect->state == PS_BUSY)
00167                       return;
00168 
00169                     eqNum = devMenu->mgr.at(i)->indi_dev.at(j)->findProp("EQUATORIAL_EOD_COORD");
00170                     
00171                     if (eqNum == NULL)
00172                     {
00173                         eqNum = devMenu->mgr.at(i)->indi_dev.at(j)->findProp("EQUATORIAL_COORD");
00174                         if (eqNum == NULL)
00175                                                 {
00176                           eqNum = devMenu->mgr.at(i)->indi_dev.at(j)->findProp("HORIZONTAL_COORD");
00177                                                   if (eqNum == NULL) continue;
00178                                                   else
00179                             useAltAz = true;
00180                         }
00181                                                 else
00182                          useJ2000 = true;
00183                     }
00184 
00185                     // make sure it has RA and DEC properties
00186                     if ( eqNum)
00187                     {
00188                         //fprintf(stderr, "Looking for RA label\n");
00189                                                 if (useAltAz)
00190                         {
00191                         lp = eqNum->findElement("AZ");
00192                         if (!lp)
00193                             continue;
00194 
00195                         dms azDMS(lp->value);
00196                         
00197                         lp = eqNum->findElement("ALT");
00198                         if (!lp)
00199                             continue;
00200 
00201                         dms altDMS(lp->value);
00202 
00203                         indi_sp.setAz(azDMS);
00204                         indi_sp.setAlt(altDMS);
00205 
00206                         }
00207                         else
00208                         {
00209 
00210                         lp = eqNum->findElement("RA");
00211                         if (!lp)
00212                             continue;
00213 
00214                         // express hours in degrees on the celestial sphere
00215                         dms raDMS(lp->value);
00216                         raDMS.setD ( raDMS.Degrees() * 15.0);
00217 
00218                         lp = eqNum->findElement("DEC");
00219                         if (!lp)
00220                             continue;
00221 
00222                         dms decDMS(lp->value);
00223 
00224 
00225                         //kdDebug() << "the KStars RA is " << raDMS.toHMSString() << endl;
00226                         //kdDebug() << "the KStars DEC is " << decDMS.toDMSString() << "\n****************" << endl;
00227 
00228                         indi_sp.setRA(raDMS);
00229                         indi_sp.setDec(decDMS);
00230 
00231                         if (useJ2000)
00232                         {
00233                             indi_sp.setRA0(raDMS);
00234                             indi_sp.setDec0(decDMS);
00235                             indi_sp.apparentCoord( (double) J2000, ksw->data()->ut().djd());
00236                         }
00237                             
00238                         if ( Options::useAltAz() ) indi_sp.EquatorialToHorizontal( ksw->LST(), ksw->geo()->lat() );
00239 
00240                         }
00241 
00242                         QPoint P = getXY( &indi_sp, Options::useAltAz(), Options::useRefraction() );
00243 
00244                         int s1 = pxperdegree/2;
00245                         int s2 = pxperdegree;
00246                         int s3 = 2*pxperdegree;
00247 
00248                         int x0 = P.x();  int y0 = P.y();
00249                         int x1 = x0 - s1/2;  int y1 = y0 - s1/2;
00250                         int x2 = x0 - s2/2;  int y2 = y0 - s2/2;
00251                         int x3 = x0 - s3/2;  int y3 = y0 - s3/2;
00252 
00253                         //Draw radial lines
00254                         psky.drawLine( x1, y0, x3, y0 );
00255                         psky.drawLine( x0+s2, y0, x0+s1/2, y0 );
00256                         psky.drawLine( x0, y1, x0, y3 );
00257                         psky.drawLine( x0, y0+s1/2, x0, y0+s2 );
00258                         //Draw circles at 0.5 & 1 degrees
00259                         psky.drawEllipse( x1, y1, s1, s1 );
00260                         psky.drawEllipse( x2, y2, s2, s2 );
00261 
00262                         psky.drawText( x0+s2 + 2 , y0, QString(devMenu->mgr.at(i)->indi_dev.at(j)->label) );
00263 
00264                     }
00265                 }
00266             }
00267         }
00268     }
00269 
00270 }
00271 
00272 void SkyMap::drawMilkyWay( QPainter& psky, double scale )
00273 {
00274     int ptsCount = 0;
00275     int mwmax = int( scale * Options::zoomFactor()/100.);
00276     int Width = int( scale * width() );
00277     int Height = int( scale * height() );
00278 
00279     int thick(1);
00280     if ( ! Options::fillMilkyWay() ) thick=3;
00281 
00282     psky.setPen( QPen( QColor( data->colorScheme()->colorNamed( "MWColor" ) ), thick, SolidLine ) );
00283     psky.setBrush( QBrush( QColor( data->colorScheme()->colorNamed( "MWColor" ) ) ) );
00284     bool offscreen, lastoffscreen=false;
00285 
00286     for ( register unsigned int j=0; j<11; ++j ) {
00287         if ( Options::fillMilkyWay() ) {
00288             ptsCount = 0;
00289             bool partVisible = false;
00290 
00291             QPoint o = getXY( data->MilkyWay[j].at(0), Options::useAltAz(), Options::useRefraction(), scale );
00292             if ( o.x() != -10000000 && o.y() != -10000000 ) pts->setPoint( ptsCount++, o.x(), o.y() );
00293             if ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) partVisible = true;
00294 
00295             for ( SkyPoint *p = data->MilkyWay[j].first(); p; p = data->MilkyWay[j].next() ) {
00296                 o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
00297                 if ( o.x() != -10000000 && o.y() != -10000000 ) pts->setPoint( ptsCount++, o.x(), o.y() );
00298                 if ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) partVisible = true;
00299             }
00300 
00301             if ( ptsCount && partVisible ) {
00302                 psky.drawPolygon( (  const QPointArray ) *pts, false, 0, ptsCount );
00303             }
00304         } else {
00305             QPoint o = getXY( data->MilkyWay[j].at(0), Options::useAltAz(), Options::useRefraction(), scale );
00306             if (o.x()==-10000000 && o.y()==-10000000) offscreen = true;
00307             else offscreen = false;
00308 
00309             psky.moveTo( o.x(), o.y() );
00310 
00311             for ( register unsigned int i=1; i<data->MilkyWay[j].count(); ++i ) {
00312                 o = getXY( data->MilkyWay[j].at(i), Options::useAltAz(), Options::useRefraction(), scale );
00313                 if (o.x()==-10000000 && o.y()==-10000000) offscreen = true;
00314                 else offscreen = false;
00315 
00316                 //don't draw a line if the last point's getXY was (-10000000, -10000000)
00317                 int dx = abs(o.x()-psky.pos().x());
00318                 int dy = abs(o.y()-psky.pos().y());
00319                 if ( (!lastoffscreen && !offscreen) && (dx<mwmax && dy<mwmax) ) {
00320                     psky.lineTo( o.x(), o.y() );
00321                 } else {
00322                     psky.moveTo( o.x(), o.y() );
00323                 }
00324                 lastoffscreen = offscreen;
00325             }
00326         }
00327     }
00328 }
00329 
00330 void SkyMap::drawCoordinateGrid( QPainter& psky, double scale )
00331 {
00332     QPoint cur;
00333 
00334     //Draw coordinate grid
00335     psky.setPen( QPen( QColor( data->colorScheme()->colorNamed( "GridColor" ) ), 1, DotLine ) ); //change to GridColor
00336 
00337     //First, the parallels
00338     for ( register double Dec=-80.; Dec<=80.; Dec += 20. ) {
00339         bool newlyVisible = false;
00340         sp->set( 0.0, Dec );
00341         if ( Options::useAltAz() ) sp->EquatorialToHorizontal( data->LST, data->geo()->lat() );
00342         QPoint o = getXY( sp, Options::useAltAz(), Options::useRefraction(), scale );
00343         QPoint o1 = o;
00344         cur = o;
00345         psky.moveTo( o.x(), o.y() );
00346 
00347         double dRA = 1./5.; //120 points along full circle of RA
00348         for ( register double RA=dRA; RA<24.; RA+=dRA ) {
00349             sp->set( RA, Dec );
00350             if ( Options::useAltAz() ) sp->EquatorialToHorizontal( data->LST, data->geo()->lat() );
00351 
00352             if ( checkVisibility( sp, guideFOV, guideXRange ) ) {
00353                 o = getXY( sp, Options::useAltAz(), Options::useRefraction(), scale );
00354 
00355                 //When drawing on the printer, the psky.pos() point does NOT get updated
00356                 //when lineTo or moveTo are called.  Grrr.  Need to store current position in QPoint cur.
00357                 int dx = cur.x() - o.x();
00358                 int dy = cur.y() - o.y();
00359                 cur = o;
00360 
00361                 if ( abs(dx) < guidemax*scale && abs(dy) < guidemax*scale ) {
00362                     if ( newlyVisible ) {
00363                         newlyVisible = false;
00364                         psky.moveTo( o.x(), o.y() );
00365                     } else {
00366                         psky.lineTo( o.x(), o.y() );
00367                     }
00368                 } else {
00369                     psky.moveTo( o.x(), o.y() );
00370                 }
00371             }
00372     }
00373 
00374         //connect the final segment
00375         int dx = psky.pos().x() - o1.x();
00376         int dy = psky.pos().y() - o1.y();
00377         if ( abs(dx) < guidemax*scale && abs(dy) < guidemax*scale ) {
00378             psky.lineTo( o1.x(), o1.y() );
00379         } else {
00380             psky.moveTo( o1.x(), o1.y() );
00381         }
00382     }
00383 
00384     //next, the meridians
00385     for ( register double RA=0.; RA<24.; RA += 2. ) {
00386         bool newlyVisible = false;
00387         SkyPoint *sp1 = new SkyPoint( RA, -90. );
00388         if ( Options::useAltAz() ) sp1->EquatorialToHorizontal( data->LST, data->geo()->lat() );
00389         QPoint o = getXY( sp1, Options::useAltAz(), Options::useRefraction(), scale );
00390         cur = o;
00391         psky.moveTo( o.x(), o.y() );
00392 
00393         double dDec = 1.;
00394         for ( register double Dec=-89.; Dec<=90.; Dec+=dDec ) {
00395             sp1->set( RA, Dec );
00396             if ( Options::useAltAz() ) sp1->EquatorialToHorizontal( data->LST, data->geo()->lat() );
00397 
00398             if ( checkVisibility( sp1, guideFOV, guideXRange ) ) {
00399                 o = getXY( sp1, Options::useAltAz(), Options::useRefraction(), scale );
00400 
00401                 //When drawing on the printer, the psky.pos() point does NOT get updated
00402                 //when lineTo or moveTo are called.  Grrr.  Need to store current position in QPoint cur.
00403                 int dx = cur.x() - o.x();
00404                 int dy = cur.y() - o.y();
00405                 cur = o;
00406 
00407                 if ( abs(dx) < guidemax*scale && abs(dy) < guidemax*scale ) {
00408                     if ( newlyVisible ) {
00409                         newlyVisible = false;
00410                         psky.moveTo( o.x(), o.y() );
00411                     } else {
00412                         psky.lineTo( o.x(), o.y() );
00413                     }
00414                 } else {
00415                     psky.moveTo( o.x(), o.y() );
00416                 }
00417             }
00418         }
00419         delete sp1;  // avoid memory leak
00420     }
00421 }
00422 
00423 void SkyMap::drawEquator( QPainter& psky, double scale )
00424 {
00425     //Draw Equator (currently can't be hidden on slew)
00426     psky.setPen( QPen( QColor( data->colorScheme()->colorNamed( "EqColor" ) ), 1, SolidLine ) );
00427 
00428     SkyPoint *p = data->Equator.first();
00429     QPoint o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
00430     QPoint o1 = o;
00431     QPoint last = o;
00432     QPoint cur = o;
00433     QPoint o2;
00434     psky.moveTo( o.x(), o.y() );
00435     bool newlyVisible = false;
00436 
00437     //index of point near the left or top/bottom edge
00438     uint index1(0), index2(0);
00439     int xSmall(width() + 100); //ridiculous initial value
00440 
00441     //start loop at second item
00442     for ( p = data->Equator.next(); p; p = data->Equator.next() ) {
00443         if ( checkVisibility( p, guideFOV, guideXRange ) ) {
00444             o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
00445 
00446             //first iteration for positioning the "Equator" label:
00447             //flag the onscreen equator point with the smallest positive x value
00448             //we don't draw the label while slewing
00449             if ( ! slewing && o.x() > 0 && o.x() < width() && o.y() > 0 && o.y() < height() ) {
00450                 if ( o.x() < xSmall ) {
00451                     xSmall = o.x();
00452                     index1 = data->Equator.at();
00453                 }
00454             }
00455 
00456             //When drawing on the printer, the psky.pos() point does NOT get updated
00457             //when lineTo or moveTo are called.  Grrr.  Need to store current position in QPoint cur.
00458             int dx = cur.x() - o.x();
00459             int dy = cur.y() - o.y();
00460             cur = o;
00461 
00462             if ( abs(dx) < guidemax*scale && abs(dy) < guidemax*scale ) {
00463                     if ( newlyVisible ) {
00464                         newlyVisible = false;
00465                         psky.moveTo( last.x(), last.y() );
00466                     }
00467                     psky.lineTo( o.x(), o.y() );
00468             } else {
00469                 psky.moveTo( o.x(), o.y() );
00470             }
00471         } else {
00472             newlyVisible = true;
00473         }
00474         last = o;
00475     }
00476 
00477     //connect the final segment
00478     int dx = psky.pos().x() - o1.x();
00479     int dy = psky.pos().y() - o1.y();
00480     if ( abs(dx) < guidemax*scale && abs(dy) < guidemax*scale ) {
00481         psky.lineTo( o1.x(), o1.y() );
00482     } else {
00483         psky.moveTo( o1.x(), o1.y() );
00484     }
00485 
00486     if ( ! slewing && xSmall < width() ) {
00487         //Draw the "Equator" label.  We have flagged the leftmost onscreen Equator point.
00488         //If the zoom level is below 1000, simply adopt this point as the anchor for the
00489         //label.  If the zoom level is 1000 or higher, we interpolate to find the exact
00490         //point at which the Equator goes offscreen, and anchor from that point.
00491         p = data->Equator.at(index1);
00492         double ra0(0.0);  //the RA of our anchor point (the Dec is known to be 0.0
00493                                             //since it's the Equator)
00494 
00495         if ( Options::zoomFactor() < 1000. ) {
00496             ra0 = p->ra()->Hours();
00497 
00498         } else {
00499             //Somewhere between Equator point p and its immediate neighbor, the Equator goes
00500             //offscreen.  Determine the exact point at which this happens.
00501             index2 = index1 + 1;
00502             if ( index2 >= data->Equator.count() ) index2 -= data->Equator.count();
00503             SkyPoint *p2 = data->Equator.at(index2);
00504 
00505             o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
00506             o2 = getXY( p2, Options::useAltAz(), Options::useRefraction(), scale );
00507 
00508             double x1, x2;
00509             //there are 3 possibilities:  (o2.x() < 0); (o2.y() < 0); (o2.y() > height())
00510             if ( o2.x() < 0 ) {
00511                 x1 = double(o.x())/double(o.x()-o2.x());
00512                 x2 = -1.0*double(o2.x())/double(o.x()-o2.x());
00513             } else if ( o2.y() < 0 ) {
00514                 x1 = double(o.y())/double(o.y()-o2.y());
00515                 x2 = -1.0*double(o2.y())/double(o.y()-o2.y());
00516             } else if ( o2.y() > height() ) {
00517                 x1 = double(height() - o.y())/double(o2.y()-o.y());
00518                 x2 = double(o2.y() - height())/double(o2.y()-o.y());
00519             } else {  //should never get here
00520                 x1 = 0.0;
00521                 x2 = 1.0;
00522             }
00523 
00524             //ra0 is the exact RA at which the Equator intersects a screen edge
00525             ra0 = x1*p2->ra()->Hours() + x2*p->ra()->Hours();
00526         }
00527 
00528         //LabelPoint is the top left corner of the text label.  It is
00529         //offset from the anchor point by -1.5 degree (0.1 hour) in RA
00530         //and -0.4 degree in Dec, scaled by 2000./zoomFactor so that they are
00531         //independent of zoom.
00532         SkyPoint LabelPoint( ra0 - 200./Options::zoomFactor(), -800./Options::zoomFactor() );
00533         if ( Options::useAltAz() )
00534             LabelPoint.EquatorialToHorizontal( data->LST, data->geo()->lat() );
00535 
00536         //p2 is a SkyPoint offset from LabelPoint in RA by -0.1 hour/zoomFactor.
00537         //We use this point to determine the rotation angle for the text (which
00538         //we want to be parallel to the line joining LabelPoint and p2)
00539         SkyPoint p2 = LabelPoint;
00540         p2.setRA( p2.ra()->Hours() - 200./Options::zoomFactor() );
00541         if ( Options::useAltAz() )
00542             p2.EquatorialToHorizontal( data->LST, data->geo()->lat() );
00543 
00544         //o and o2 are the screen coordinates of LabelPoint and p2.
00545         o = getXY( &LabelPoint, Options::useAltAz(), Options::useRefraction(), scale );
00546         o2 = getXY( &p2, Options::useAltAz(), Options::useRefraction() );
00547 
00548         double sx = double( o.x() - o2.x() );
00549         double sy = double( o.y() - o2.y() );
00550         double angle;
00551         if ( sx ) {
00552             angle = atan( sy/sx )*180.0/dms::PI;
00553         } else {
00554             angle = 90.0;
00555             if ( sy < 0 ) angle = -90.0;
00556         }
00557 
00558         //Finally, draw the "Equator" label at the determined location and angle
00559         psky.save();
00560         psky.translate( o.x(), o.y() );
00561         psky.rotate( double( angle ) );  //rotate the coordinate system
00562         psky.drawText( 0, 0, i18n( "Equator" ) );
00563         psky.restore(); //reset coordinate system
00564     }
00565 }
00566 
00567 void SkyMap::drawEcliptic( QPainter& psky, double scale )
00568 {
00569     int Width = int( scale * width() );
00570     int Height = int( scale * height() );
00571 
00572     //Draw Ecliptic (currently can't be hidden on slew)
00573     psky.setPen( QPen( QColor( data->colorScheme()->colorNamed( "EclColor" ) ), 1, SolidLine ) );
00574 
00575     SkyPoint *p = data->Ecliptic.first();
00576     QPoint o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
00577     QPoint o2 = o;
00578     QPoint o1 = o;
00579     QPoint last = o;
00580     QPoint cur = o;
00581     psky.moveTo( o.x(), o.y() );
00582 
00583     //index of point near the right or top/bottom edge
00584     uint index1(0), index2(0);
00585     int xBig(-100); //ridiculous initial value
00586 
00587     bool newlyVisible = false;
00588     //Start loop at second item
00589     for ( p = data->Ecliptic.next(); p; p = data->Ecliptic.next() ) {
00590         if ( checkVisibility( p, guideFOV, guideXRange ) ) {
00591             o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
00592 
00593             //first iteration for positioning the "Ecliptic" label:
00594             //flag the onscreen equator point with the largest x value
00595             //we don't draw the label while slewing
00596             if ( ! slewing && o.x() > 0 && o.x() < width() && o.y() > 0 && o.y() < height() ) {
00597                 if ( o.x() > xBig ) {
00598                     xBig = o.x();
00599                     index1 = data->Ecliptic.at();
00600                 }
00601             }
00602 
00603             //When drawing on the printer, the psky.pos() point does NOT get updated
00604             //when lineTo or moveTo are called.  Grrr.  Need to store current position in QPoint cur.
00605             int dx = cur.x() - o.x();
00606             int dy = cur.y() - o.y();
00607             cur = o;
00608 
00609             if ( abs(dx) < guidemax*scale && abs(dy) < guidemax*scale ) {
00610                 if ( newlyVisible ) {
00611                     newlyVisible = false;
00612                     psky.moveTo( last.x(), last.y() );
00613                 }
00614                 psky.lineTo( o.x(), o.y() );
00615             } else {
00616                 psky.moveTo( o.x(), o.y() );
00617             }
00618         } else {
00619             newlyVisible = true;
00620         }
00621         last = o;
00622     }
00623 
00624     //connect the final segment
00625     int dx = psky.pos().x() - o1.x();
00626     int dy = psky.pos().y() - o1.y();
00627     if ( abs(dx) < Width && abs(dy) < Height ) {
00628         psky.lineTo( o1.x(), o1.y() );
00629     } else {
00630         psky.moveTo( o1.x(), o1.y() );
00631     }
00632 
00633     if ( ! slewing && xBig > 0 ) {
00634         //Draw the "Ecliptic" label.  We have flagged the rightmost onscreen Ecliptic point.
00635         //If the zoom level is below 1000, simply adopt this point as the anchor for the
00636         //label.  If the zoom level is 1000 or higher, we interpolate to find the exact
00637         //point at which the Ecliptic goes offscreen, and anchor from that point.
00638         p = data->Ecliptic.at(index1);
00639         double ra0(0.0);  //the ra of our anchor point
00640         double dec0(0.0); //the dec of our anchor point
00641 
00642         if ( Options::zoomFactor() < 1000. ) {
00643             ra0 = p->ra()->Hours();
00644             dec0 = p->dec()->Degrees();
00645         } else {
00646             //Somewhere between Ecliptic point p and its immediate neighbor, the Ecliptic goes
00647             //offscreen.  Determine the exact point at which this happens.
00648             if ( index1 == 0 ) index2 = 0;
00649             else index2 = index1 - 1;
00650             SkyPoint *p2 = data->Ecliptic.at(index2);
00651 
00652             o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
00653             o2 = getXY( p2, Options::useAltAz(), Options::useRefraction(), scale );
00654 
00655             double x1, x2;
00656             //there are 3 possibilities:  (o2.x() > width()); (o2.y() < 0); (o2.y() > height())
00657             if ( o2.x() > width() ) {
00658                 x1 = double(width()-o.x())/double(o2.x()-o.x());
00659                 x2 = double(o2.x()-width())/double(o2.x()-o.x());
00660             } else if ( o2.y() < 0 ) {
00661                 x1 = double(o.y())/double(o.y()-o2.y());
00662                 x2 = -1.0*double(o2.y())/double(o.y()-o2.y());
00663             } else if ( o2.y() > height() ) {
00664                 x1 = double(height() - o.y())/double(o2.y()-o.y());
00665                 x2 = double(o2.y() - height())/double(o2.y()-o.y());
00666             } else {  //should never get here
00667                 x1 = 0.0;
00668                 x2 = 1.0;
00669             }
00670 
00671             //ra0 is the exact RA at which the Ecliptic intersects a screen edge
00672             ra0 = x1*p2->ra()->Hours() + x2*p->ra()->Hours();
00673             //dec0 is the exact Dec at which the Ecliptic intersects a screen edge
00674             dec0 = x1*p2->dec()->Degrees() + x2*p->dec()->Degrees();
00675         }
00676 
00677         KSNumbers num( data->ut().djd() );
00678         dms ecLong, ecLat;
00679 
00680         //LabelPoint is offset from the anchor point by +2.0 degree ecl. Long
00681         //and -0.4 degree in ecl. Lat, scaled by 2000./zoomFactor so that they are
00682         //independent of zoom.
00683         SkyPoint LabelPoint(ra0, dec0);
00684         LabelPoint.findEcliptic( num.obliquity(), ecLong, ecLat );
00685         ecLong.setD( ecLong.Degrees() + 4000./Options::zoomFactor() );
00686         ecLat.setD( ecLat.Degrees() - 800./Options::zoomFactor() );
00687         LabelPoint.setFromEcliptic( num.obliquity(), &ecLong, &ecLat );
00688         if ( Options::useAltAz() )
00689             LabelPoint.EquatorialToHorizontal( data->LST, data->geo()->lat() );
00690 
00691         //p2 is a SkyPoint offset from LabelPoint by -1.0 degrees of ecliptic longitude.
00692         //we use p2 to determine the onscreen rotation angle for the ecliptic label,
00693         //which we want to be parallel to the line between LabelPoint and p2.
00694         SkyPoint p2(ra0, dec0);
00695         p2.findEcliptic( num.obliquity(), ecLong, ecLat );
00696         ecLong.setD( ecLong.Degrees() + 2000./Options::zoomFactor() );
00697         ecLat.setD( ecLat.Degrees() - 800./Options::zoomFactor() );
00698         p2.setFromEcliptic( num.obliquity(), &ecLong, &ecLat );
00699         if ( Options::useAltAz() )
00700             p2.EquatorialToHorizontal( data->LST, data->geo()->lat() );
00701 
00702         //o and o2 are the screen positions of LabelPoint and p2.
00703         o = getXY( &LabelPoint, Options::useAltAz(), Options::useRefraction(), scale );
00704         o2 = getXY( &p2, Options::useAltAz(), Options::useRefraction() );
00705 
00706         double sx = double( o.x() - o2.x() );
00707         double sy = double( o.y() - o2.y() );
00708         double angle;
00709         if ( sx ) {
00710             angle = atan( sy/sx )*180.0/dms::PI;
00711         } else {
00712             angle = 90.0;
00713             if ( sy < 0 ) angle = -90.0;
00714         }
00715 
00716         //Finally, draw the "Ecliptic" label at the determined location and angle
00717         psky.save();
00718         psky.translate( o.x(), o.y() );
00719         psky.rotate( double( angle ) );  //rotate the coordinate system
00720         psky.drawText( 0, 0, i18n( "Ecliptic" ) );
00721         psky.restore(); //reset coordinate system
00722     }
00723 }
00724 
00725 void SkyMap::drawHorizon( QPainter& psky, double scale )
00726 {
00727     int Width = int( scale * width() );
00728     int Height = int( scale * height() );
00729 
00730     QPtrList<QPoint> points;
00731     points.setAutoDelete(true);
00732     QPoint o, o2;
00733 
00734     //Draw Horizon
00735     //The horizon should not be corrected for atmospheric refraction, so getXY has doRefract=false...
00736     if (Options::showHorizon() || Options::showGround() ) {
00737         QPoint OutLeft(0,0), OutRight(0,0);
00738 
00739         psky.setPen( QPen( QColor( data->colorScheme()->colorNamed( "HorzColor" ) ), 1, SolidLine ) );
00740         psky.setBrush( QColor ( data->colorScheme()->colorNamed( "HorzColor" ) ) );
00741         int ptsCount = 0;
00742         int maxdist = int(Options::zoomFactor()/4);
00743 
00744         //index of point near the right or top/bottom edge
00745         uint index1(0), index2(0);
00746         int xBig(-100); //ridiculous initial value
00747 
00748         for ( SkyPoint *p = data->Horizon.first(); p; p = data->Horizon.next() ) {
00749             o = getXY( p, Options::useAltAz(), false, scale );  //false: do not refract the horizon
00750             bool found = false;
00751 
00752             //first iteration for positioning the "Horizon" label:
00753             //flag the onscreen equator point with the largest x value
00754             //we don't draw the label while slewing, or if the opaque ground is drawn
00755             if ( ! slewing && ( ! Options::showGround() || ! Options::useAltAz() )
00756                         && o.x() > 0 && o.x() < width() && o.y() > 0 && o.y() < height() ) {
00757                 if ( o.x() > xBig ) {
00758                     xBig = o.x();
00759                     index1 = data->Horizon.at();
00760                 }
00761             }
00762 
00763             //Use the QPtrList of points to pre-sort visible horizon points
00764             if ( o.x() > -100 && o.x() < Width + 100 && o.y() > -100 && o.y() < Height + 100 ) {
00765                 if ( Options::useAltAz() ) {
00766                     register unsigned int j;
00767                     for ( j=0; j<points.count(); ++j ) {
00768                         if ( o.x() < points.at(j)->x() ) {
00769                             found = true;
00770                             break;
00771                         }
00772                     }
00773                     if ( found ) {
00774                         points.insert( j, new QPoint(o) );
00775                     } else {
00776                         points.append( new QPoint(o) );
00777                     }
00778                 } else {
00779                     points.append( new QPoint(o) );
00780                 }
00781             } else {  //find the out-of-bounds points closest to the left and right borders
00782                 if ( ( OutLeft.x() == 0 || o.x() > OutLeft.x() ) && o.x() < -100 ) {
00783                     OutLeft.setX( o.x() );
00784                     OutLeft.setY( o.y() );
00785                 }
00786                 if ( ( OutRight.x() == 0 || o.x() < OutRight.x() ) && o.x() >  + 100 ) {
00787                     OutRight.setX( o.x() );
00788                     OutRight.setY( o.y() );
00789                 }
00790             }
00791         }
00792 
00793         //Add left-edge and right-edge points based on interpolating the first/last onscreen points
00794         //to the nearest offscreen points.
00795 
00796         if ( Options::useAltAz() && points.count() > 0 ) {
00797             //If the edge of the visible sky circle is onscreen, then we should 
00798             //interpolate the first and last horizon points to the edge of the circle.
00799             //Otherwise, interpolate to the screen edge.
00800             double dx = 0.5*double(Width)/Options::zoomFactor(); //center-to-edge ang in radians
00801             double r0 = 2.0*sin(0.25*dms::PI);
00802             
00803             if ( dx < r0 ) { //edge of visible sky circle is not visible
00804                 //Interpolate from first sorted onscreen point to x=-100,
00805                 //using OutLeft to determine the slope
00806                 int xtotal = ( points.at( 0 )->x() - OutLeft.x() );
00807                 int xx = ( points.at( 0 )->x() + 100 ) / xtotal;
00808                 int yp = xx*OutRight.y() + (1-xx)*points.at( 0 )->y();  //interpolated left-edge y value
00809                 QPoint *LeftEdge = new QPoint( -100, yp );
00810                 points.insert( 0, LeftEdge ); //Prepend LeftEdge to the beginning of points
00811     
00812                 //Interpolate from the last sorted onscreen point to ()+100,
00813                 //using OutRight to determine the slope.
00814                 xtotal = ( OutRight.x() - points.at( points.count() - 1 )->x() );
00815                 xx = ( Width + 100 - points.at( points.count() - 1 )->x() ) / xtotal;
00816                 yp = xx*OutRight.y() + (1-xx)*points.at( points.count() - 1 )->y(); //interpolated right-edge y value
00817                 QPoint *RightEdge = new QPoint( Width+100, yp );
00818                 points.append( RightEdge );
00819             }
00820         //If there are no horizon points, then either the horizon doesn't pass through the screen
00821         //or we're at high zoom, and horizon points lie on either side of the screen.
00822         } else if ( Options::useAltAz() && OutLeft.y() !=0 && OutRight.y() !=0 &&
00823                         !( OutLeft.y() > Height + 100 && OutRight.y() > Height + 100 ) &&
00824                         !( OutLeft.y() < -100 && OutRight.y() < -100 ) ) {
00825 
00826             //It's possible at high zoom that /no/ horizon points are onscreen.  In this case,
00827             //interpolate between OutLeft and OutRight directly to construct the horizon polygon.
00828             int xtotal = ( OutRight.x() - OutLeft.x() );
00829             int xx = ( OutRight.x() + 100 ) / xtotal;
00830             int yp = xx*OutLeft.y() + (1-xx)*OutRight.y();  //interpolated left-edge y value
00831 //          QPoint *LeftEdge = new QPoint( -100, yp );
00832             points.append( new QPoint( -100, yp ) );
00833 
00834             xx = ( Width + 100 - OutLeft.x() ) / xtotal;
00835             yp = xx*OutRight.y() + (1-xx)*OutLeft.y(); //interpolated right-edge y value
00836 //          QPoint *RightEdge = new QPoint( Width+100, yp );
00837             points.append( new QPoint( Width+100, yp ) );
00838         }
00839 
00840         if ( points.count() ) {
00841 //      Fill the pts array with sorted horizon points, Draw Horizon Line
00842             pts->setPoint( 0, points.at(0)->x(), points.at(0)->y() );
00843             if ( Options::showHorizon() ) psky.moveTo( points.at(0)->x(), points.at(0)->y() );
00844 
00845             for ( register unsigned int i=1; i<points.count(); ++i ) {
00846                 pts->setPoint( i, points.at(i)->x(), points.at(i)->y() );
00847 
00848                 if ( Options::showHorizon() ) {
00849                     if ( !Options::useAltAz() && ( abs( points.at(i)->x() - psky.pos().x() ) > maxdist ||
00850                                 abs( points.at(i)->y() - psky.pos().y() ) > maxdist ) ) {
00851                         psky.moveTo( points.at(i)->x(), points.at(i)->y() );
00852                     } else {
00853                         psky.lineTo( points.at(i)->x(), points.at(i)->y() );
00854                     }
00855 
00856                 }
00857             }
00858 
00859             //connect the last segment back to the beginning
00860             if ( abs( points.at(0)->x() - psky.pos().x() ) < maxdist && abs( points.at(0)->y() - psky.pos().y() ) < maxdist )
00861                 psky.lineTo( points.at(0)->x(), points.at(0)->y() );
00862 
00863             //Finish the Ground polygon.  If sky edge is visible, the 
00864             //bottom edge follows the sky circle.  Otherwise, we just 
00865             //add a square bottom edge, offscreen
00866             if ( Options::useAltAz() ) {
00867                 if ( Options::showGround() ) {
00868                     ptsCount = points.count();
00869                     
00870                     //center-to-edge ang in radians
00871                     double dx = 0.5*double(Width)/Options::zoomFactor(); 
00872                     double r0 = 2.0*sin(0.25*dms::PI);
00873                     
00874 //                  //Second QPointsArray for blocking the region outside the sky circle
00875 //                  QPointArray bpts( 100 ); //need 90 points along sky circle, plus 4 to complete polygon
00876 //                  uint bpCount(0);
00877                     
00878                     if ( dx > r0 ) { //sky edge is visible
00879                         for ( double t=360.; t >= 180.; t-=2. ) {  //add points along edge of circle
00880                             dms a( t );
00881                             double sa(0.), ca(0.);
00882                             a.SinCos( sa, ca );
00883                             int xx = Width/2 + int(r0*Options::zoomFactor()*ca);
00884                             int yy = Height/2 - int(r0*Options::zoomFactor()*sa);
00885                             
00886                             pts->setPoint( ptsCount++, xx, yy );
00887 //                          bpts.setPoint( bpCount++, xx, yy );
00888                         }
00889                         
00890 //                      //complete the background polygon, then draw it with SkyColor
00891 //                      bpts.setPoint( bpCount++,        -100, Height/2     );
00892 //                      bpts.setPoint( bpCount++,        -100, Height + 100 );
00893 //                      bpts.setPoint( bpCount++, Width + 100, Height + 100 );
00894 //                      bpts.setPoint( bpCount++, Width + 100, Height/2     );
00895 //                      psky.setPen( QColor ( data->colorScheme()->colorNamed( "SkyColor" ) ) );
00896 //                      psky.setBrush( QColor ( data->colorScheme()->colorNamed( "SkyColor" ) ) );
00897 //                      psky.drawPolygon( bpts, false, 0, bpCount );
00898 //                      //Reset colors for Horizon polygon
00899 //                      psky.setPen( QColor ( data->colorScheme()->colorNamed( "HorzColor" ) ) );
00900 //                      psky.setBrush( QColor ( data->colorScheme()->colorNamed( "HorzColor" ) ) );
00901                     
00902                     } else {
00903                         pts->setPoint( ptsCount++, Width+100, Height+100 );   //bottom right corner
00904                         pts->setPoint( ptsCount++, -100, Height+100 );         //bottom left corner
00905                     }
00906                         
00907                     //Draw the Horizon polygon
00908                     psky.drawPolygon( ( const QPointArray ) *pts, false, 0, ptsCount );
00909 
00910                     //remove all items in points list
00911                     for ( register unsigned int i=0; i<points.count(); ++i ) {
00912                         points.remove(i);
00913                     }
00914                 }
00915 
00916 //  Draw compass heading labels along horizon
00917                 SkyPoint *c = new SkyPoint;
00918                 QPoint cpoint;
00919 
00920                 if ( Options::showGround() )
00921                     psky.setPen( QColor ( data->colorScheme()->colorNamed( "CompassColor" ) ) );
00922                 else
00923                     psky.setPen( QColor ( data->colorScheme()->colorNamed( "HorzColor" ) ) );
00924 
00925         //North
00926                 c->setAz( 359.99 );
00927                 c->setAlt( 0.0 );
00928                 if ( !Options::useAltAz() ) c->HorizontalToEquatorial( data->LST, data->geo()->lat() );
00929                 cpoint = getXY( c, Options::useAltAz(), false, scale );
00930                 cpoint.setY( cpoint.y() + int(scale*20) );
00931                 if (cpoint.x() > 0 && cpoint.x() < Width && cpoint.y() > 0 && cpoint.y() < Height ) {
00932                     psky.drawText( cpoint.x(), cpoint.y(), i18n( "North", "N" ) );
00933                 }
00934 
00935         //NorthEast
00936                 c->setAz( 45.0 );
00937                 c->setAlt( 0.0 );
00938                 if ( !Options::useAltAz() ) c->HorizontalToEquatorial( data->LST, data->geo()->lat() );
00939                 cpoint = getXY( c, Options::useAltAz(), false, scale );
00940                 cpoint.setY( cpoint.y() + int(scale*20) );
00941                 if (cpoint.x() > 0 && cpoint.x() < Width && cpoint.y() > 0 && cpoint.y() < Height ) {
00942                     psky.drawText( cpoint.x(), cpoint.y(), i18n( "Northeast", "NE" ) );
00943                 }
00944 
00945         //East
00946                 c->setAz( 90.0 );
00947                 c->setAlt( 0.0 );
00948                 if ( !Options::useAltAz() ) c->HorizontalToEquatorial( data->LST, data->geo()->lat() );
00949                 cpoint = getXY( c, Options::useAltAz(), false, scale );
00950                 cpoint.setY( cpoint.y() + int(scale*20) );
00951                 if (cpoint.x() > 0 && cpoint.x() < Width && cpoint.y() > 0 && cpoint.y() < Height ) {
00952                     psky.drawText( cpoint.x(), cpoint.y(), i18n( "East", "E" ) );
00953                 }
00954 
00955         //SouthEast
00956                 c->setAz( 135.0 );
00957                 c->setAlt( 0.0 );
00958                 if ( !Options::useAltAz() ) c->HorizontalToEquatorial( data->LST, data->geo()->lat() );
00959                 cpoint = getXY( c, Options::useAltAz(), false, scale );
00960                 cpoint.setY( cpoint.y() + int(scale*20) );
00961                 if (cpoint.x() > 0 && cpoint.x() < Width && cpoint.y() > 0 && cpoint.y() < Height ) {
00962                     psky.drawText( cpoint.x(), cpoint.y(), i18n( "Southeast", "SE" ) );
00963                 }
00964 
00965         //South
00966                 c->setAz( 179.99 );
00967                 c->setAlt( 0.0 );
00968                 if ( !Options::useAltAz() ) c->HorizontalToEquatorial( data->LST, data->geo()->lat() );
00969                 cpoint = getXY( c, Options::useAltAz(), false, scale );
00970                 cpoint.setY( cpoint.y() + int(scale*20) );
00971                 if (cpoint.x() > 0 && cpoint.x() < Width && cpoint.y() > 0 && cpoint.y() < Height ) {
00972                     psky.drawText( cpoint.x(), cpoint.y(), i18n( "South", "S" ) );
00973                 }
00974 
00975         //SouthWest
00976                 c->setAz( 225.0 );
00977                 c->setAlt( 0.0 );
00978                 if ( !Options::useAltAz() ) c->HorizontalToEquatorial( data->LST, data->geo()->lat() );
00979                 cpoint = getXY( c, Options::useAltAz(), false, scale );
00980                 cpoint.setY( cpoint.y() + int(scale*20) );
00981                 if (cpoint.x() > 0 && cpoint.x() < Width && cpoint.y() > 0 && cpoint.y() < Height ) {
00982                     psky.drawText( cpoint.x(), cpoint.y(), i18n( "Southwest", "SW" ) );
00983                 }
00984 
00985         //West
00986                 c->setAz( 270.0 );
00987                 c->setAlt( 0.0 );
00988                 if ( !Options::useAltAz() ) c->HorizontalToEquatorial( data->LST, data->geo()->lat() );
00989                 cpoint = getXY( c, Options::useAltAz(), false, scale );
00990                 cpoint.setY( cpoint.y() + int(scale*20) );
00991                 if (cpoint.x() > 0 && cpoint.x() < Width && cpoint.y() > 0 && cpoint.y() < Height ) {
00992                     psky.drawText( cpoint.x(), cpoint.y(), i18n( "West", "W" ) );
00993                 }
00994 
00995         //NorthWest
00996                 c->setAz( 315.0 );
00997                 c->setAlt( 0.0 );
00998                 if ( !Options::useAltAz() ) c->HorizontalToEquatorial( data->LST, data->geo()->lat() );
00999                 cpoint = getXY( c, Options::useAltAz(), false, scale );
01000                 cpoint.setY( cpoint.y() + int(scale*20) );
01001                 if (cpoint.x() > 0 && cpoint.x() < Width && cpoint.y() > 0 && cpoint.y() < Height ) {
01002                     psky.drawText( cpoint.x(), cpoint.y(), i18n( "Northwest", "NW" ) );
01003                 }
01004 
01005                 delete c;
01006             }
01007         }
01008 
01009         if ( ! slewing && (! Options::showGround() || ! Options::useAltAz() ) && xBig > 0 ) {
01010             //Draw the "Horizon" label.  We have flagged the rightmost onscreen Horizon point.
01011             //If the zoom level is below 1000, simply adopt this point as the anchor for the
01012             //label.  If the zoom level is 1000 or higher, we interpolate to find the exact
01013             //point at which the Horizon goes offscreen, and anchor from that point.
01014             SkyPoint *p = data->Horizon.at(index1);
01015             double ra0(0.0);  //the ra of our anchor point
01016             double dec0(0.0); //the dec of our anchor point
01017 
01018             if ( Options::zoomFactor() < 1000. ) {
01019                 ra0 = p->ra()->Hours();
01020                 dec0 = p->dec()->Degrees();
01021             } else {
01022                 //Somewhere between Horizon point p and its immediate neighbor, the Horizon goes
01023                 //offscreen.  Determine the exact point at which this happens.
01024                 index2 = index1 + 1;
01025                 if ( data->Horizon.count() &&  index2 > data->Horizon.count() - 1 ) index2 -= data->Horizon.count();
01026                 SkyPoint *p2 = data->Horizon.at(index2);
01027 
01028                 QPoint o1 = getXY( p, Options::useAltAz(), false, scale );
01029                 QPoint o2 = getXY( p2, Options::useAltAz(), false, scale );
01030 
01031                 double x1, x2;
01032                 //there are 3 possibilities:  (o2.x() > width()); (o2.y() < 0); (o2.y() > height())
01033                 if ( o2.x() > width() ) {
01034                     x1 = double(width()-o1.x())/double(o2.x()-o1.x());
01035                     x2 = double(o2.x()-width())/double(o2.x()-o1.x());
01036                 } else if ( o2.y() < 0 ) {
01037                     x1 = double(o1.y())/double(o1.y()-o2.y());
01038                     x2 = -1.0*double(o2.y())/double(o1.y()-o2.y());
01039                 } else if ( o2.y() > height() ) {
01040                     x1 = double(height() - o1.y())/double(o2.y()-o1.y());
01041                     x2 = double(o2.y() - height())/double(o2.y()-o1.y());
01042                 } else {  //should never get here
01043                     x1 = 0.0;
01044                     x2 = 1.0;
01045                 }
01046 
01047                 //ra0 is the exact RA at which the Horizon intersects a screen edge
01048                 ra0 = x1*p2->ra()->Hours() + x2*p->ra()->Hours();
01049                 //dec0 is the exact Dec at which the Horizon intersects a screen edge
01050                 dec0 = x1*p2->dec()->Degrees() + x2*p->dec()->Degrees();
01051             }
01052 
01053             //LabelPoint is offset from the anchor point by -2.0 degrees in azimuth
01054             //and -0.4 degree altitude, scaled by 2000./zoomFactor so that they are
01055             //independent of zoom.
01056             SkyPoint LabelPoint(ra0, dec0);
01057             LabelPoint.EquatorialToHorizontal( data->LST, data->geo()->lat() );
01058             LabelPoint.setAlt( LabelPoint.alt()->Degrees() - 800./Options::zoomFactor() );
01059             LabelPoint.setAz( LabelPoint.az()->Degrees() - 4000./Options::zoomFactor() );
01060             LabelPoint.HorizontalToEquatorial( data->LST, data->geo()->lat() );
01061             o = getXY( &LabelPoint, Options::useAltAz(), false, scale );
01062             if ( o.x() > width() || o.x() < 0 ) {
01063                 //the LabelPoint is offscreen.  Either we are in the Southern hemisphere,
01064                 //or the sky is rotated upside-down.  Use an azimuth offset of +2.0 degrees
01065             LabelPoint.setAlt( LabelPoint.alt()->Degrees() + 1600./Options::zoomFactor() );
01066                 LabelPoint.setAz( LabelPoint.az()->Degrees() + 8000./Options::zoomFactor() );
01067                 LabelPoint.HorizontalToEquatorial( data->LST, data->geo()->lat() );
01068                 o = getXY( &LabelPoint, Options::useAltAz(), false, scale );
01069             }
01070 
01071             //p2 is a skypoint offset from LabelPoint by +/-1 degree azimuth (scaled by
01072             //2000./zoomFactor).  We use p2 to determine the rotation angle for the
01073             //Horizon label, which we want to be parallel to the line between LabelPoint and p2.
01074             SkyPoint p2( LabelPoint.ra(), LabelPoint.dec() );
01075             p2.EquatorialToHorizontal( data->LST, data->geo()->lat() );
01076             p2.setAz( p2.az()->Degrees() + 2000./Options::zoomFactor() );
01077             p2.HorizontalToEquatorial( data->LST, data->geo()->lat() );
01078 
01079             //o and o2 are the screen positions of LabelPoint and p2
01080             o = getXY( &LabelPoint, Options::useAltAz(), false, scale );
01081             o2 = getXY( &p2, Options::useAltAz(), false, scale );
01082 
01083             double sx = double( o.x() - o2.x() );
01084             double sy = double( o.y() - o2.y() );
01085             double angle;
01086             if ( sx ) {
01087                 angle = atan( sy/sx )*180.0/dms::PI;
01088             } else {
01089                 angle = 90.0;
01090                 if ( sy < 0 ) angle = -90.0;
01091             }
01092 
01093             //Finally, draw the "Equator" label at the determined location and angle
01094             psky.save();
01095             psky.translate( o.x(), o.y() );
01096             psky.rotate( double( angle ) );  //rotate the coordinate system
01097             psky.drawText( 0, 0, i18n( "Horizon" ) );
01098             psky.restore(); //reset coordinate system
01099         }
01100     }  //endif drawing horizon
01101 }
01102 
01103 void SkyMap::drawConstellationLines( QPainter& psky, double scale )
01104 {
01105     int Width = int( scale * width() );
01106     int Height = int( scale * height() );
01107 
01108     //Draw Constellation Lines
01109     psky.setPen( QPen( QColor( data->colorScheme()->colorNamed( "CLineColor" ) ), 1, SolidLine ) ); //change to colorGrid
01110     int iLast = -1;
01111 
01112     for ( SkyPoint *p = data->clineList.first(); p; p = data->clineList.next() ) {
01113         QPoint o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
01114 
01115         if ( ( o.x() >= -1000 && o.x() <= Width+1000 && o.y() >=-1000 && o.y() <= Height+1000 ) ) {
01116             if ( data->clineModeList.at(data->clineList.at())->latin1()=='M' ) {
01117                 psky.moveTo( o.x(), o.y() );
01118             } else if ( data->clineModeList.at(data->clineList.at())->latin1()=='D' ) {
01119                 if ( data->clineList.at()== (int)(iLast+1) ) {
01120                     psky.lineTo( o.x(), o.y() );
01121                 } else {
01122                     psky.moveTo( o.x(), o.y() );
01123                 }
01124             }
01125             iLast = data->clineList.at();
01126         }
01127   }
01128 }
01129 
01130 void SkyMap::drawConstellationBoundaries( QPainter &psky, double scale ) {
01131     int Width = int( scale * width() );
01132     int Height = int( scale * height() );
01133 
01134     psky.setPen( QPen( QColor( data->colorScheme()->colorNamed( "CBoundColor" ) ), 1, SolidLine ) );
01135 
01136     for ( CSegment *seg = data->csegmentList.first(); seg; seg = data->csegmentList.next() ) {
01137         bool started( false );
01138         SkyPoint *p = seg->firstNode();
01139         QPoint o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
01140         if ( ( o.x() >= -1000 && o.x() <= Width+1000 && o.y() >=-1000 && o.y() <= Height+1000 ) ) {
01141             psky.moveTo( o.x(), o.y() );
01142             started = true;
01143         }
01144 
01145         for ( p = seg->nextNode(); p; p = seg->nextNode() ) {
01146             QPoint o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
01147 
01148             if ( ( o.x() >= -1000 && o.x() <= Width+1000 && o.y() >=-1000 && o.y() <= Height+1000 ) ) {
01149                 if ( started ) {
01150                     psky.lineTo( o.x(), o.y() );
01151                 } else {
01152                     psky.moveTo( o.x(), o.y() );
01153                     started = true;
01154                 }
01155             } else {
01156                 started = false;
01157             }
01158         }
01159     }
01160 }
01161 
01162 void SkyMap::drawConstellationNames( QPainter& psky, double scale ) {
01163     int Width = int( scale * width() );
01164     int Height = int( scale * height() );
01165 
01166     //Draw Constellation Names
01167     psky.setPen( QColor( data->colorScheme()->colorNamed( "CNameColor" ) ) );
01168     for ( SkyObject *p = data->cnameList.first(); p; p = data->cnameList.next() ) {
01169         if ( checkVisibility( p, fov(), XRange ) ) {
01170             QPoint o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
01171             if (o.x() >= 0 && o.x() <= Width && o.y() >=0 && o.y() <= Height ) {
01172                 if ( Options::useLatinConstellNames() ) {
01173                     int dx = 5*p->name().length();
01174                     psky.drawText( o.x()-dx, o.y(), p->name() );  // latin constellation names
01175                 } else if ( Options::useLocalConstellNames() ) {
01176                     // can't use translatedName() because we need the context string in i18n()
01177                     int dx = 5*( i18n( "Constellation name (optional)", p->name().local8Bit().data() ).length() );
01178                     psky.drawText( o.x()-dx, o.y(), i18n( "Constellation name (optional)", p->name().local8Bit().data() ) ); // localized constellation names
01179                 } else {
01180                     int dx = 5*p->name2().length();
01181                     psky.drawText( o.x()-dx, o.y(), p->name2() ); //name2 is the IAU abbreviation
01182                 }
01183             }
01184         }
01185   }
01186 }
01187 
01188 void SkyMap::drawStars( QPainter& psky, double scale ) {
01189     int Width = int( scale * width() );
01190     int Height = int( scale * height() );
01191 
01192     bool checkSlewing = ( ( slewing || ( clockSlewing && data->clock()->isActive() ) )
01193                 && Options::hideOnSlew() );
01194 
01195 //shortcuts to inform wheter to draw different objects
01196     bool hideFaintStars( checkSlewing && Options::hideStars() );
01197 
01198     if ( Options::showStars() ) {
01199         //adjust maglimit for ZoomLevel
01200         double lgmin = log10(MINZOOM);
01201         double lgmax = log10(MAXZOOM);
01202         double lgz = log10(Options::zoomFactor());
01203 
01204         double maglim = Options::magLimitDrawStar();
01205         if ( lgz <= 0.75*lgmax ) maglim -= (Options::magLimitDrawStar() - Options::magLimitDrawStarZoomOut() )*(0.75*lgmax - lgz)/(0.75*lgmax - lgmin);
01206         float sizeFactor = 6.0 + (lgz - lgmin);
01207 
01208         for ( StarObject *curStar = data->starList.first(); curStar; curStar = data->starList.next() ) {
01209             // break loop if maglim is reached
01210             if ( curStar->mag() > maglim || ( hideFaintStars && curStar->mag() > Options::magLimitHideStar() ) ) break;
01211 
01212             if ( checkVisibility( curStar, fov(), XRange ) ) {
01213                 QPoint o = getXY( curStar, Options::useAltAz(), Options::useRefraction(), scale );
01214 
01215                 // draw star if currently on screen
01216                 if (o.x() >= 0 && o.x() <= Width && o.y() >=0 && o.y() <= Height ) {
01217                     int size = int( scale * ( sizeFactor*( maglim - curStar->mag())/maglim ) + 1 );
01218 
01219                     if ( size > 0 ) {
01220                         QChar c = curStar->color();
01221                         QPixmap *spixmap = starpix->getPixmap( &c, size );
01222                         curStar->draw( psky, sky, spixmap, o.x(), o.y(), true, scale );
01223 
01224                         // now that we have drawn the star, we can display some extra info
01225                         //don't label unnamed stars with the generic "star" name
01226                         bool drawName = ( Options::showStarNames() && (curStar->name() != i18n("star") ) );
01227                         if ( !checkSlewing && (curStar->mag() <= Options::magLimitDrawStarInfo() )
01228                                 && ( drawName || Options::showStarMagnitudes() ) ) {
01229 
01230                             psky.setPen( QColor( data->colorScheme()->colorNamed( "SNameColor" ) ) );
01231                             QFont stdFont( psky.font() );
01232                             QFont smallFont( stdFont );
01233                             smallFont.setPointSize( stdFont.pointSize() - 2 );
01234                             if ( Options::zoomFactor() < 10.*MINZOOM ) {
01235                                 psky.setFont( smallFont );
01236                             } else {
01237                                 psky.setFont( stdFont );
01238                             }
01239 
01240                             curStar->drawLabel( psky, o.x(), o.y(), Options::zoomFactor(),
01241                                     drawName, Options::showStarMagnitudes(), scale );
01242 
01243                             //reset font
01244                             psky.setFont( stdFont );
01245                         }
01246                     }
01247                 }
01248             }
01249         }
01250     }
01251 }
01252 
01253 void SkyMap::drawDeepSkyCatalog( QPainter& psky, QPtrList<DeepSkyObject>& catalog, QColor& color,
01254             bool drawObject, bool drawImage, double scale )
01255 {
01256     if ( drawObject || drawImage ) {  //don't do anything if nothing is to be drawn!
01257         int Width = int( scale * width() );
01258         int Height = int( scale * height() );
01259 
01260         // Set color once
01261         psky.setPen( color );
01262         psky.setBrush( NoBrush );
01263         QColor colorHST  = data->colorScheme()->colorNamed( "HSTColor" );
01264 
01265         double maglim = Options::magLimitDrawDeepSky();
01266 
01267         //FIXME
01268         //disabling faint limits until the NGC/IC catalog has reasonable mags
01269         //adjust maglimit for ZoomLevel
01270         //double lgmin = log10(MINZOOM);
01271         //double lgmax = log10(MAXZOOM);
01272         //double lgz = log10(Options::zoomFactor());
01273         //if ( lgz <= 0.75*lgmax ) maglim -= (Options::magLimitDrawDeepSky() - Options::magLimitDrawDeepSkyZoomOut() )*(0.75*lgmax - lgz)/(0.75*lgmax - lgmin);
01274         //else
01275             maglim = 40.0; //show all deep-sky objects
01276 
01277         for ( DeepSkyObject *obj = catalog.first(); obj; obj = catalog.next() ) {
01278             if ( checkVisibility( obj, fov(), XRange ) ) {
01279                 float mag = obj->mag();
01280                 //only draw objects if flags set and its brighter than maglim (unless mag is undefined (=99.9)
01281                 if ( mag > 90.0 || mag < (float)maglim ) {
01282                     QPoint o = getXY( obj, Options::useAltAz(), Options::useRefraction(), scale );
01283                     if ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) {
01284                         double PositionAngle = findPA( obj, o.x(), o.y(), scale );
01285 
01286                         //Draw Image
01287                         if ( drawImage && Options::zoomFactor() > 5.*MINZOOM ) {
01288                             obj->drawImage( psky, o.x(), o.y(), PositionAngle, Options::zoomFactor(), scale );
01289                         }
01290 
01291                         //Draw Symbol
01292                         if ( drawObject ) {
01293                             //change color if extra images are available
01294                             // most objects don't have those, so we only change colors temporarily
01295                             // for the few exceptions. Changing color is expensive!!!
01296                             bool bColorChanged = false;
01297                             if ( obj->isCatalogM() && obj->ImageList.count() > 1 ) {
01298                                 psky.setPen( colorHST );
01299                                 bColorChanged = true;
01300                             } else if ( (!obj->isCatalogM()) && obj->ImageList.count() ) {
01301                                 psky.setPen( colorHST );
01302                                 bColorChanged = true;
01303                             }
01304 
01305                             obj->drawSymbol( psky, o.x(), o.y(), PositionAngle, Options::zoomFactor(), scale );
01306 
01307                             // revert temporary color change
01308                             if ( bColorChanged ) {
01309                                 psky.setPen( color );
01310                             }
01311                         }
01312                     }
01313                 }
01314             } else { //Object failed checkVisible(); delete it's Image pointer, if it exists.
01315                 if ( obj->image() ) {
01316                     obj->deleteImage();
01317                 }
01318             }
01319         }
01320     }
01321 }
01322 
01323 void SkyMap::drawDeepSkyObjects( QPainter& psky, double scale )
01324 {
01325     int Width = int( scale * width() );
01326     int Height = int( scale * height() );
01327 
01328     QImage ScaledImage;
01329 
01330     bool checkSlewing = ( ( slewing || ( clockSlewing && data->clock()->isActive() ) )
01331                 && Options::hideOnSlew() );
01332 
01333 //shortcuts to inform wheter to draw different objects
01334     bool drawMess( Options::showDeepSky() && ( Options::showMessier() || Options::showMessierImages() ) && !(checkSlewing && Options::hideMessier() ) );
01335     bool drawNGC( Options::showDeepSky() && Options::showNGC() && !(checkSlewing && Options::hideNGC() ) );
01336     bool drawOther( Options::showDeepSky() && Options::showOther() && !(checkSlewing && Options::hideOther() ) );
01337     bool drawIC( Options::showDeepSky() && Options::showIC() && !(checkSlewing && Options::hideIC() ) );
01338     bool drawImages( Options::showMessierImages() );
01339 
01340     // calculate color objects once, outside the loop
01341     QColor colorMess = data->colorScheme()->colorNamed( "MessColor" );
01342     QColor colorIC  = data->colorScheme()->colorNamed( "ICColor" );
01343     QColor colorNGC  = data->colorScheme()->colorNamed( "NGCColor" );
01344 
01345     // draw Messier catalog
01346     if ( drawMess ) {
01347         drawDeepSkyCatalog( psky, data->deepSkyListMessier, colorMess, Options::showMessier(), drawImages, scale );
01348     }
01349 
01350     // draw NGC Catalog
01351     if ( drawNGC ) {
01352         drawDeepSkyCatalog( psky, data->deepSkyListNGC, colorNGC, true, drawImages, scale );
01353     }
01354 
01355     // draw IC catalog
01356     if ( drawIC ) {
01357         drawDeepSkyCatalog( psky, data->deepSkyListIC, colorIC, true, drawImages, scale );
01358     }
01359 
01360     // draw the rest
01361     if ( drawOther ) {
01362         //Use NGC color for now...
01363         drawDeepSkyCatalog( psky, data->deepSkyListOther, colorNGC, true, drawImages, scale );
01364     }
01365 
01366     //Draw Custom Catalogs
01367     for ( register unsigned int i=0; i<data->CustomCatalogs.count(); ++i ) { 
01368         if ( Options::showCatalog()[i] ) {
01369             QPtrList<SkyObject> cat = data->CustomCatalogs.at(i)->objList();
01370 
01371             for ( SkyObject *obj = cat.first(); obj; obj = cat.next() ) {
01372 
01373                 if ( checkVisibility( obj, fov(), XRange ) ) {
01374                     QPoint o = getXY( obj, Options::useAltAz(), Options::useRefraction(), scale );
01375 
01376                     if ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) {
01377 
01378                         if ( obj->type()==0 ) {
01379                             StarObject *starobj = (StarObject*)obj;
01380                             float zoomlim = 7.0 + ( Options::zoomFactor()/MINZOOM)/50.0;
01381                             float mag = starobj->mag();
01382                             float sizeFactor = 2.0;
01383                             int size = int( sizeFactor*(zoomlim - mag) ) + 1;
01384                             if (size>23) size=23;
01385                             if ( size > 0 ) {
01386                                 QChar c = starobj->color();
01387                                 QPixmap *spixmap = starpix->getPixmap( &c, size );
01388 
01389                                 // Try to paint with the selected color
01390                                 spixmap->fill(QColor( data->CustomCatalogs.at(i)->color() ));
01391                                 starobj->draw( psky, sky, spixmap, o.x(), o.y(), true, scale );
01392                                 // After drawing the star we display some extra info like
01393                                 // the name ...
01394                                 bool drawName = ( Options::showStarNames() && (starobj->name() != i18n("star") ) );
01395                                 if ( !checkSlewing && (starobj->mag() <= Options::magLimitDrawStarInfo() )
01396                                         && ( drawName || Options::showStarMagnitudes() ) ) {
01397 
01398                                             psky.setPen( QColor( data->colorScheme()->colorNamed( "SNameColor" ) ) );
01399                                             QFont stdFont( psky.font() );
01400                                             QFont smallFont( stdFont );
01401                                             smallFont.setPointSize( stdFont.pointSize() - 2 );
01402                                             if ( Options::zoomFactor() < 10.*MINZOOM ) {
01403                                                 psky.setFont( smallFont );
01404                                             } else {
01405                                                 psky.setFont( stdFont );
01406                                             }
01407 
01408                                             starobj->drawLabel( psky, o.x(), o.y(), Options::zoomFactor(), drawName, Options::showStarMagnitudes(), scale );
01409 
01410                                             //reset font
01411                                             psky.setFont( stdFont );
01412                                 }
01413                             }
01414                         } else {
01415                             DeepSkyObject *dso = (DeepSkyObject*)obj;
01416                             double pa = findPA( dso, o.x(), o.y(), scale );
01417                             dso->drawImage( psky, o.x(), o.y(), pa, Options::zoomFactor() );
01418 
01419                             psky.setBrush( NoBrush );
01420                             psky.setPen( QColor( data->CustomCatalogs.at(i)->color() ) );
01421 
01422                             dso->drawSymbol( psky, o.x(), o.y(), pa, Options::zoomFactor() );
01423                         }
01424                     }
01425                 }
01426             }
01427         }
01428     }
01429 }
01430 
01431 void SkyMap::drawAttachedLabels( QPainter &psky, double scale ) {
01432     int Width = int( scale * width() );
01433     int Height = int( scale * height() );
01434 
01435     psky.setPen( data->colorScheme()->colorNamed( "UserLabelColor" ) );
01436 
01437     bool checkSlewing = ( slewing || ( clockSlewing && data->clock()->isActive() ) ) && Options::hideOnSlew();
01438     bool drawPlanets( Options::showPlanets() && !(checkSlewing && Options::hidePlanets() ) );
01439     bool drawComets( drawPlanets && Options::showComets() );
01440     bool drawAsteroids( drawPlanets && Options::showAsteroids() );
01441     bool drawMessier( Options::showDeepSky() && ( Options::showMessier() || Options::showMessierImages() ) && !(checkSlewing && Options::hideMessier() ) );
01442     bool drawNGC( Options::showDeepSky() && Options::showNGC() && !(checkSlewing && Options::hideNGC() ) );
01443     bool drawIC( Options::showDeepSky() && Options::showIC() && !(checkSlewing && Options::hideIC() ) );
01444     bool drawOther( Options::showDeepSky() && Options::showOther() && !(checkSlewing && Options::hideOther() ) );
01445     bool drawSAO = ( Options::showStars() );
01446     bool hideFaintStars( checkSlewing && Options::hideStars() );
01447 
01448     for ( SkyObject *obj = data->ObjLabelList.first(); obj; obj = data->ObjLabelList.next() ) {
01449         //Only draw an attached label if the object is being drawn to the map
01450         //reproducing logic from other draw funcs here...not an optimal solution
01451         if ( obj->type() == SkyObject::STAR ) {
01452             if ( ! drawSAO ) return;
01453             if ( obj->mag() > Options::magLimitDrawStar() ) return;
01454             if ( hideFaintStars && obj->mag() > Options::magLimitHideStar() ) return;
01455         }
01456         if ( obj->type() == SkyObject::PLANET ) {
01457             if ( ! drawPlanets ) return;
01458             if ( obj->name() == "Sun" && ! Options::showSun() ) return;
01459             if ( obj->name() == "Mercury" && ! Options::showMercury() ) return;
01460             if ( obj->name() == "Venus" && ! Options::showVenus() ) return;
01461             if ( obj->name() == "Moon" && ! Options::showMoon() ) return;
01462             if ( obj->name() == "Mars" && ! Options::showMars() ) return;
01463             if ( obj->name() == "Jupiter" && ! Options::showJupiter() ) return;
01464             if ( obj->name() == "Saturn" && ! Options::showSaturn() ) return;
01465             if ( obj->name() == "Uranus" && ! Options::showUranus() ) return;
01466             if ( obj->name() == "Neptune" && ! Options::showNeptune() ) return;
01467             if ( obj->name() == "Pluto" && ! Options::showPluto() ) return;
01468         }
01469         if ( obj->type() >= SkyObject::OPEN_CLUSTER && obj->type() <= SkyObject::GALAXY ) {
01470             if ( ((DeepSkyObject*)obj)->isCatalogM() && ! drawMessier ) return;
01471             if ( ((DeepSkyObject*)obj)->isCatalogNGC() && ! drawNGC ) return;
01472             if ( ((DeepSkyObject*)obj)->isCatalogIC() && ! drawIC ) return;
01473             if ( ((DeepSkyObject*)obj)->isCatalogNone() && ! drawOther ) return;
01474         }
01475         if ( obj->type() == SkyObject::COMET && ! drawComets ) return;
01476         if ( obj->type() == SkyObject::ASTEROID && ! drawAsteroids ) return;
01477 
01478         if ( checkVisibility( obj, fov(), XRange ) ) {
01479             QPoint o = getXY( obj, Options::useAltAz(), Options::useRefraction(), scale );
01480             if ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) {
01481                 drawNameLabel( psky, obj, o.x(), o.y(), scale );
01482             }
01483         }
01484     }
01485 
01486     //Attach a label to the centered object
01487     if ( focusObject() != NULL && Options::useAutoLabel() ) {
01488         QPoint o = getXY( focusObject(), Options::useAltAz(), Options::useRefraction(), scale );
01489         if ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height )
01490             drawNameLabel( psky, focusObject(), o.x(), o.y(), scale );
01491     }
01492 }
01493 
01494 void SkyMap::drawNameLabel( QPainter &psky, SkyObject *obj, int x, int y, double scale ) {
01495     int size(0);
01496 
01497     QFont stdFont( psky.font() );
01498     QFont smallFont( stdFont );
01499     smallFont.setPointSize( stdFont.pointSize() - 2 );
01500     if ( Options::zoomFactor() < 10.*MINZOOM ) {
01501         psky.setFont( smallFont );
01502     } else {
01503         psky.setFont( stdFont );
01504     }
01505 
01506     //Stars
01507     if ( obj->type() == SkyObject::STAR ) {
01508         ((StarObject*)obj)->drawLabel( psky, x, y, Options::zoomFactor(), true, false, scale );
01509         psky.setFont( stdFont );
01510         return;
01511 
01512     //Solar system
01513     } else if ( obj->type() == SkyObject::PLANET
01514                         || obj->type() == SkyObject::ASTEROID
01515                         || obj->type() == SkyObject::COMET ) {
01516         KSPlanetBase *p = (KSPlanetBase*)obj;
01517         size = int( p->angSize() * scale * dms::PI * Options::zoomFactor()/10800.0 );
01518         int minsize = 4;
01519         if ( p->type() == SkyObject::ASTEROID || p->type() == SkyObject::COMET )
01520             minsize = 2;
01521         if ( p->name() == "Sun" || p->name() == "Moon" )
01522             minsize = 8;
01523         if ( size < minsize )
01524             size = minsize;
01525         if ( p->name() == "Saturn" )
01526             size = int(2.5*size);
01527 
01528     //Other
01529     } else {
01530         //Calculate object size in pixels
01531         float majorAxis = ((DeepSkyObject*)obj)->a();
01532         if ( majorAxis == 0.0 && obj->type() == 1 ) majorAxis = 1.0; //catalog stars
01533         size = int( majorAxis * scale * dms::PI * Options::zoomFactor()/10800.0 );
01534     }
01535 
01536     int offset = int( ( 0.5*size + 4 ) );
01537     psky.drawText( x+offset, y+offset, obj->translatedName() );
01538 
01539     //Reset font
01540     psky.setFont( stdFont );
01541 }
01542 
01543 void SkyMap::drawPlanetTrail( QPainter& psky, KSPlanetBase *ksp, double scale )
01544 {
01545     if ( ksp->hasTrail() ) {
01546         int Width = int( scale * width() );
01547         int Height = int( scale * height() );
01548 
01549         QColor tcolor1 = QColor( data->colorScheme()->colorNamed( "PlanetTrailColor" ) );
01550         QColor tcolor2 = QColor( data->colorScheme()->colorNamed( "SkyColor" ) );
01551 
01552         SkyPoint *p = ksp->trail()->first();
01553         QPoint o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
01554         QPoint cur( o );
01555 
01556         bool doDrawLine(false);
01557         int i = 0;
01558         int n = ksp->trail()->count();
01559 
01560         if ( ( o.x() >= -1000 && o.x() <= Width+1000 && o.y() >=-1000 && o.y() <= Height+1000 ) ) {
01561             psky.moveTo(o.x(), o.y());
01562             doDrawLine = true;
01563         }
01564 
01565         psky.setPen( QPen( tcolor1, 1 ) );
01566         for ( p = ksp->trail()->next(); p; p = ksp->trail()->next() ) {
01567             if ( Options::fadePlanetTrails() ) {
01568                 //Define interpolated color
01569                 QColor tcolor = QColor(
01570                             (i*tcolor1.red()   + (n-i)*tcolor2.red())/n,
01571                             (i*tcolor1.green() + (n-i)*tcolor2.green())/n,
01572                             (i*tcolor1.blue()  + (n-i)*tcolor2.blue())/n );
01573                 ++i;
01574                 psky.setPen( QPen( tcolor, 1 ) );
01575             }
01576 
01577             o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
01578             if ( ( o.x() >= -1000 && o.x() <= Width+1000 && o.y() >=-1000 && o.y() <= Height+1000 ) ) {
01579 
01580                 //Want to disable line-drawing if this point and the last are both outside bounds of display.
01581                 if ( ! rect().contains( o ) && ! rect().contains( cur ) ) doDrawLine = false;
01582                 cur = o;
01583 
01584                 if ( doDrawLine ) {
01585                     psky.lineTo( o.x(), o.y() );
01586                 } else {
01587                     psky.moveTo( o.x(), o.y() );
01588                     doDrawLine = true;
01589                 }
01590             }
01591         }
01592     }
01593 }
01594 
01595 void SkyMap::drawSolarSystem( QPainter& psky, bool drawPlanets, double scale )
01596 {
01597     int Width = int( scale * width() );
01598     int Height = int( scale * height() );
01599 
01600     if ( drawPlanets ) {
01601         //Draw all trails first so they never appear "in front of" solar system bodies
01602         //draw Trail
01603         if ( Options::showSun() && data->PCat->findByName("Sun")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Sun"), scale );
01604         if ( Options::showMercury() && data->PCat->findByName("Mercury")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Mercury"), scale );
01605         if ( Options::showVenus() && data->PCat->findByName("Venus")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Venus"), scale );
01606         if ( Options::showMoon() && data->Moon->hasTrail() ) drawPlanetTrail( psky, data->Moon, scale );
01607         if ( Options::showMars() && data->PCat->findByName("Mars")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Mars"), scale );
01608         if ( Options::showJupiter() && data->PCat->findByName("Jupiter")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Jupiter"), scale );
01609         if ( Options::showSaturn() && data->PCat->findByName("Saturn")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Saturn"), scale );
01610         if ( Options::showUranus() && data->PCat->findByName("Uranus")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Uranus"), scale );
01611         if ( Options::showNeptune() && data->PCat->findByName("Neptune")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Neptune"), scale );
01612         if ( Options::showPluto() && data->PCat->findByName("Pluto")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Pluto"), scale );
01613         if ( Options::showAsteroids() ) {
01614             for ( KSAsteroid *ast = data->asteroidList.first(); ast; ast = data->asteroidList.next() ) {
01615                 if ( ast->mag() > Options::magLimitAsteroid() ) break;
01616                 if ( ast->hasTrail() ) drawPlanetTrail( psky, ast, scale );
01617             }
01618         }
01619         if ( Options::showComets() ) {
01620             for ( KSComet *com = data->cometList.first(); com; com = data->cometList.next() ) {
01621                 if ( com->hasTrail() ) drawPlanetTrail( psky, com, scale );
01622             }
01623         }
01624 
01625         //Now draw the actual solar system bodies.  Draw furthest to closest.
01626         //Draw Asteroids
01627         if ( Options::showAsteroids() ) {
01628             for ( KSAsteroid *ast = data->asteroidList.first(); ast; ast = data->asteroidList.next() ) {
01629                 if ( ast->mag() > Options::magLimitAsteroid() ) break;
01630 
01631                 if ( checkVisibility( ast, fov(), XRange ) ) {
01632                     psky.setPen( QPen( QColor( "gray" ) ) );
01633                     psky.setBrush( QBrush( QColor( "gray" ) ) );
01634                     QPoint o = getXY( ast, Options::useAltAz(), Options::useRefraction(), scale );
01635 
01636                     if ( ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) ) {
01637                         int size = int( ast->angSize() * scale * dms::PI * Options::zoomFactor()/10800.0 );
01638                         if ( size < 1 ) size = 1;
01639                         int x1 = o.x() - size/2;
01640                         int y1 = o.y() - size/2;
01641                         psky.drawEllipse( x1, y1, size, size );
01642 
01643                         //draw Name
01644                         if ( Options::showAsteroidNames() && ast->mag() < Options::magLimitAsteroidName() ) {
01645                             psky.setPen( QColor( data->colorScheme()->colorNamed( "PNameColor" ) ) );
01646                             drawNameLabel( psky, ast, o.x(), o.y(), scale );
01647                         }
01648                     }
01649                 }
01650             }
01651         }
01652 
01653         //Draw Comets
01654         if ( Options::showComets() ) {
01655             for ( KSComet *com = data->cometList.first(); com; com = data->cometList.next() ) {
01656                 if ( checkVisibility( com, fov(), XRange ) ) {
01657                     psky.setPen( QPen( QColor( "cyan4" ) ) );
01658                     psky.setBrush( QBrush( QColor( "cyan4" ) ) );
01659                     QPoint o = getXY( com, Options::useAltAz(), Options::useRefraction(), scale );
01660 
01661                     if ( ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) ) {
01662                         int size = int( com->angSize() * scale * dms::PI * Options::zoomFactor()/10800.0 );
01663                         if ( size < 1 ) size = 1;
01664                         int x1 = o.x() - size/2;
01665                         int y1 = o.y() - size/2;
01666                         psky.drawEllipse( x1, y1, size, size );
01667 
01668                         //draw Name
01669                         if ( Options::showCometNames() && com->rsun() < Options::maxRadCometName() ) {
01670                             psky.setPen( QColor( data->colorScheme()->colorNamed( "PNameColor" ) ) );
01671                             drawNameLabel( psky, com, o.x(), o.y(), scale );
01672                         }
01673                     }
01674                 }
01675             }
01676         }
01677 
01678         //Draw Pluto
01679         if ( Options::showPluto() ) {
01680             drawPlanet(psky, data->PCat->findByName("Pluto"), QColor( "gray" ), 50.*MINZOOM, 1, scale );
01681         }
01682 
01683         //Draw Neptune
01684         if ( Options::showNeptune() ) {
01685             drawPlanet(psky, data->PCat->findByName("Neptune"), QColor( "SkyBlue" ), 20.*MINZOOM, 1, scale );
01686         }
01687 
01688         //Draw Uranus
01689         if ( Options::showUranus() ) {
01690             drawPlanet(psky, data->PCat->findByName("Uranus"), QColor( "LightSeaGreen" ), 20.*MINZOOM, 1, scale );
01691         }
01692 
01693         //Draw Saturn
01694         if ( Options::showSaturn() ) {
01695             drawPlanet(psky, data->PCat->findByName("Saturn"), QColor( "LightYellow2" ), 20.*MINZOOM, 2, scale );
01696         }
01697 
01698         //Draw Jupiter and its moons.
01699         //Draw all moons first, then Jupiter, then redraw moons that are in front of Jupiter.
01700         if ( Options::showJupiter() ) {
01701             //Draw Jovian moons
01702             psky.setPen( QPen( QColor( "white" ) ) );
01703             if ( Options::zoomFactor() > 10.*MINZOOM ) {
01704                 for ( unsigned int i=0; i<4; ++i ) {
01705                     QPoint o = getXY( data->jmoons->pos(i), Options::useAltAz(), Options::useRefraction(), scale );
01706                     if ( ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) ) {
01707                         psky.drawEllipse( o.x()-1, o.y()-1, 2, 2 );
01708                     }
01709                 }
01710             }
01711 
01712             drawPlanet(psky, data->PCat->findByName("Jupiter"), QColor( "Goldenrod" ), 20.*MINZOOM, 1, scale );
01713 
01714             //Re-draw Jovian moons which are in front of Jupiter, also draw all 4 moon labels.
01715             psky.setPen( QPen( QColor( "white" ) ) );
01716             if ( Options::zoomFactor() > 10.*MINZOOM ) {
01717                 QFont pfont = psky.font();
01718                 QFont moonFont = psky.font();
01719                 moonFont.setPointSize( pfont.pointSize() - 2 );
01720                 psky.setFont( moonFont );
01721 
01722                 for ( unsigned int i=0; i<4; ++i ) {
01723                     QPoint o = getXY( data->jmoons->pos(i), Options::useAltAz(), Options::useRefraction(), scale );
01724                     if ( ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) ) {
01725                         if ( data->jmoons->z(i) < 0.0 ) //Moon is nearer than Jupiter
01726                             psky.drawEllipse( o.x()-1, o.y()-1, 2, 2 );
01727 
01728                         //Draw Moon name labels if at high zoom
01729                         if ( Options::showPlanetNames() && Options::zoomFactor() > 50.*MINZOOM ) {
01730                             int offset = int(3*scale);
01731                             psky.drawText( o.x() + offset, o.y() + offset, data->jmoons->name(i) );
01732                         }
01733                     }
01734                 }
01735 
01736                 //reset font
01737                 psky.setFont( pfont );
01738             }
01739         }
01740 
01741         //Draw Mars
01742         if ( Options::showMars() ) {
01743             drawPlanet(psky, data->PCat->findByName("Mars"), QColor( "Red" ), 20.*MINZOOM, 1, scale );
01744         }
01745 
01746         //For the inner planets, we need to determine the distance-order
01747         //because the order can change with time
01748         double rv = data->PCat->findByName("Venus")->rearth();
01749         double rm = data->PCat->findByName("Mercury")->rearth();
01750         double rs = data->PCat->findByName("Sun")->rearth();
01751         unsigned int iv(0), im(0), is(0);
01752         if ( rm > rs ) im++;
01753         if ( rm > rv ) im++;
01754         if ( rv > rs ) iv++;
01755         if ( rv > rm ) iv++;
01756         if ( rs > rm ) is++;
01757         if ( rs > rv ) is++;
01758 
01759         for ( unsigned int i=0; i<3; i++ ) {
01760             if ( i==is ) {
01761                 //Draw Sun
01762                 if ( Options::showSun() ) {
01763                     drawPlanet(psky, data->PCat->findByName("Sun"), QColor( "Yellow" ), MINZOOM, 1, scale );
01764                 }
01765             } else if ( i==im ) {
01766                 //Draw Mercury
01767                 if ( Options::showMercury() ) {
01768                     drawPlanet(psky, data->PCat->findByName("Mercury"), QColor( "SlateBlue1" ), 20.*MINZOOM, 1, scale );
01769                 }
01770             } else if ( i==iv ) {
01771                 //Draw Venus
01772                 if ( Options::showVenus() ) {
01773                     drawPlanet(psky, data->PCat->findByName("Venus"), QColor( "LightGreen" ), 20.*MINZOOM, 1, scale );
01774                 }
01775             }
01776         }
01777 
01778         //Draw Moon
01779         if ( Options::showMoon() ) {
01780             drawPlanet(psky, data->Moon, QColor( "White" ), MINZOOM, 1, scale );
01781         }
01782     }
01783 }
01784 
01785 void SkyMap::drawPlanet( QPainter &psky, KSPlanetBase *p, QColor c,
01786         double zoommin, int resize_mult, double scale ) {
01787 
01788     if ( checkVisibility( p, fov(), XRange ) ) {
01789         int Width = int( scale * width() );
01790         int Height = int( scale * height() );
01791 
01792         int sizemin = 4;
01793         if ( p->name() == "Sun" || p->name() == "Moon" ) sizemin = 8;
01794         sizemin = int( sizemin * scale );
01795 
01796         psky.setPen( c );
01797         psky.setBrush( c );
01798         QPoint o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
01799 
01800         //Is planet onscreen?
01801         if ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) {
01802             int size = int( p->angSize() * scale * dms::PI * Options::zoomFactor()/10800.0 );
01803             if ( size < sizemin ) size = sizemin;
01804 
01805             //Draw planet image if:
01806             if ( Options::showPlanetImages() &&  //user wants them,
01807                     int(Options::zoomFactor()) >= int(zoommin) &&  //zoomed in enough,
01808                     !p->image()->isNull() &&  //image loaded ok,
01809                     size < Width ) {  //and size isn't too big.
01810 
01811                 //Image size must be modified to account for possibility that rotated image's
01812                 //size is bigger than original image size.  The rotated image is a square
01813                 //superscribed on the original image.  The superscribed square is larger than
01814                 //the original square by a factor of (cos(t) + sin(t)) where t is the angle by
01815                 //which the two squares are rotated (in our case, equal to the position angle +
01816                 //the north angle, reduced between 0 and 90 degrees).
01817                 //The proof is left as an exercise to the student :)
01818                 dms pa( findPA( p, o.x(), o.y(), scale ) );
01819                 double spa, cpa;
01820                 pa.SinCos( spa, cpa );
01821                 cpa = fabs(cpa);
01822                 spa = fabs(spa);
01823                 size = int( size * (cpa + spa) );
01824 
01825                 //Because Saturn has rings, we inflate its image size by a factor 2.5
01826                 if ( p->name() == "Saturn" ) size = int(2.5*size);
01827 
01828                 if (resize_mult != 1) {
01829                     size *= resize_mult;
01830                 }
01831 
01832                 p->scaleRotateImage( size, pa.Degrees() );
01833                 int x1 = o.x() - p->image()->width()/2;
01834                 int y1 = o.y() - p->image()->height()/2;
01835                 psky.drawImage( x1, y1, *(p->image()));
01836 
01837             } else {                                   //Otherwise, draw a simple circle.
01838 
01839                 psky.drawEllipse( o.x()-size/2, o.y()-size/2, size, size );
01840             }
01841 
01842             //draw Name
01843             if ( Options::showPlanetNames() ) {
01844                 psky.setPen( QColor( data->colorScheme()->colorNamed( "PNameColor" ) ) );
01845                 drawNameLabel( psky, p, o.x(), o.y(), scale );
01846             }
01847         }
01848     }
01849 }
01850 
01851 void SkyMap::exportSkyImage( const QPaintDevice *pd ) {
01852     QPainter p;
01853 
01854     //shortcuts to inform wheter to draw different objects
01855     bool drawPlanets( Options::showPlanets() );
01856     bool drawMW( Options::showMilkyWay() );
01857     bool drawCNames( Options::showCNames() );
01858     bool drawCLines( Options::showCLines() );
01859     bool drawCBounds( Options::showCBounds() );
01860     bool drawGrid( Options::showGrid() );
01861 
01862     p.begin( pd );
01863     QPaintDeviceMetrics pdm( p.device() );
01864 
01865     //scale image such that it fills 90% of the x or y dimension on the paint device
01866     double xscale = double(pdm.width()) / double(width());
01867     double yscale = double(pdm.height()) / double(height());
01868     double scale = (xscale < yscale) ? xscale : yscale;
01869 
01870     int pdWidth = int( scale * width() );
01871     int pdHeight = int( scale * height() );
01872     int x1 = int( 0.5*(pdm.width()  - pdWidth) );
01873     int y1 = int( 0.5*(pdm.height()  - pdHeight) );
01874 
01875     p.setClipRect( QRect( x1, y1, pdWidth, pdHeight ) );
01876     p.setClipping( true );
01877 
01878     //Fill background with sky color
01879     p.fillRect( x1, y1, pdWidth, pdHeight, QBrush( data->colorScheme()->colorNamed( "SkyColor" ) ) );
01880 
01881     if ( x1 || y1 ) p.translate( x1, y1 );
01882 
01883     if ( drawMW ) drawMilkyWay( p, scale );
01884     if ( drawGrid ) drawCoordinateGrid( p, scale );
01885 
01886     if ( drawCBounds ) drawConstellationBoundaries( p, scale );
01887     if ( drawCLines ) drawConstellationLines( p, scale );
01888     if ( drawCNames ) drawConstellationNames( p, scale );
01889 
01890     if ( Options::showEquator() ) drawEquator( p, scale );
01891     if ( Options::showEcliptic() ) drawEcliptic( p, scale );
01892 
01893     drawStars( p, scale );
01894     drawDeepSkyObjects( p, scale );
01895     drawSolarSystem( p, drawPlanets, scale );
01896     drawAttachedLabels( p, scale );
01897     drawObservingList( p, scale );
01898     drawHorizon( p, scale );
01899 
01900     p.end();
01901 }
01902 
01903 void SkyMap::setMapGeometry() {
01904     guidemax = int(Options::zoomFactor()/10.0);
01905 
01906     isPoleVisible = false;
01907     if ( Options::useAltAz() ) {
01908         XRange = 1.2*fov()/cos( focus()->alt()->radians() );
01909         Ymax = fabs( focus()->alt()->Degrees() ) + fov();
01910     } else {
01911         XRange = 1.2*fov()/cos( focus()->dec()->radians() );
01912         Ymax = fabs( focus()->dec()->Degrees() ) + fov();
01913     }
01914     if ( Ymax >= 90. ) isPoleVisible = true;
01915 
01916     //at high zoom, double FOV for guide lines so they don't disappear.
01917     guideFOV = fov();
01918     guideXRange = XRange;
01919     if ( Options::zoomFactor() > 10.*MINZOOM ) { guideFOV *= 2.0; guideXRange *= 2.0; }
01920 }

kstars

Skip menu "kstars"
  • Main Page
  • Modules
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • keduca
  • kstars
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal