You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeedu/kstars/kstars/skymapdraw.cpp

1921 lines
70 KiB

/***************************************************************************
skymapdraw.cpp - Trinity Desktop Planetarium
-------------------
begin : Sun Mar 2 2003
copyright : (C) 2001 by Jason Harris
email : jharris@30doradus.org
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
//This file contains drawing functions SkyMap class.
#include <stdlib.h> // abs
#include <math.h> //log10()
#include <iostream>
#include <tqpaintdevicemetrics.h>
#include <tqpainter.h>
#include "skymap.h"
#include "Options.h"
#include "kstars.h"
#include "kstarsdata.h"
#include "ksnumbers.h"
#include "skyobject.h"
#include "deepskyobject.h"
#include "starobject.h"
#include "ksplanetbase.h"
#include "ksasteroid.h"
#include "kscomet.h"
#include "ksmoon.h"
#include "jupitermoons.h"
#include "infoboxes.h"
#include "simclock.h"
#include "csegment.h"
#include "customcatalog.h"
#include "devicemanager.h"
#include "indimenu.h"
#include "indiproperty.h"
#include "indielement.h"
#include "indidevice.h"
void SkyMap::drawOverlays( TQPixmap *pm ) {
if ( ksw ) { //only if we are drawing in the GUI window
TQPainter p;
p.begin( pm );
showFocusCoords( true );
drawBoxes( p );
//draw FOV symbol
ksw->data()->fovSymbol.draw( p, (float)(Options::fOVSize() * Options::zoomFactor()/57.3/60.0) );
drawTelescopeSymbols( p );
drawObservingList( p );
drawZoomBox( p );
if ( transientObject() ) drawTransientLabel( p );
if (isAngleMode()) {
updateAngleRuler();
drawAngleRuler( p );
}
}
}
void SkyMap::drawAngleRuler( TQPainter &p ) {
p.setPen( TQPen( data->colorScheme()->colorNamed( "AngularRuler" ), 1, DotLine ) );
p.drawLine( beginRulerPoint, endRulerPoint );
}
void SkyMap::drawZoomBox( TQPainter &p ) {
//draw the manual zoom-box, if it exists
if ( ZoomRect.isValid() ) {
p.setPen( TQPen( "white", 1, DotLine ) );
p.drawRect( ZoomRect.x(), ZoomRect.y(), ZoomRect.width(), ZoomRect.height() );
}
}
void SkyMap::drawTransientLabel( TQPainter &p ) {
if ( transientObject() ) {
p.setPen( TransientColor );
if ( checkVisibility( transientObject(), fov(), XRange ) ) {
TQPoint o = getXY( transientObject(), Options::useAltAz(), Options::useRefraction(), 1.0 );
if ( o.x() >= 0 && o.x() <= width() && o.y() >= 0 && o.y() <= height() ) {
drawNameLabel( p, transientObject(), o.x(), o.y(), 1.0 );
}
}
}
}
void SkyMap::drawBoxes( TQPainter &p ) {
if ( ksw ) { //only if we are drawing in the GUI window
ksw->infoBoxes()->drawBoxes( p,
data->colorScheme()->colorNamed( "BoxTextColor" ),
data->colorScheme()->colorNamed( "BoxGrabColor" ),
data->colorScheme()->colorNamed( "BoxBGColor" ), Options::boxBGMode() );
}
}
void SkyMap::drawObservingList( TQPainter &psky, double scale ) {
psky.setPen( TQPen( TQColor( data->colorScheme()->colorNamed( "ObsListColor" ) ), 1 ) );
if ( ksw && ksw->observingList()->count() ) {
for ( SkyObject* obj = ksw->observingList()->first(); obj; obj = ksw->observingList()->next() ) {
if ( checkVisibility( obj, fov(), XRange ) ) {
TQPoint o = getXY( obj, Options::useAltAz(), Options::useRefraction() );
// label object if it is currently on screen
if (o.x() >= 0 && o.x() <= width() && o.y() >=0 && o.y() <= height() ) {
if ( Options::obsListSymbol() ) {
int size = int(20*scale);
int x1 = o.x() - size/2;
int y1 = o.y() - size/2;
psky.drawArc( x1, y1, size, size, -60*16, 120*16 );
psky.drawArc( x1, y1, size, size, 120*16, 120*16 );
}
if ( Options::obsListText() ) {
drawNameLabel( psky, obj, o.x(), o.y(), scale );
}
}
}
}
}
}
void SkyMap::drawTelescopeSymbols(TQPainter &psky) {
if ( ksw ) { //ksw doesn't exist in non-GUI mode!
INDI_P *eqNum;
INDI_P *portConnect;
INDI_E *lp;
INDIMenu *devMenu = ksw->getINDIMenu();
bool useJ2000 (false), useAltAz(false);
SkyPoint indi_sp;
if (!Options::indiCrosshairs() || devMenu == NULL)
return;
psky.setPen( TQPen( TQColor( data->colorScheme()->colorNamed("TargetColor" ) ) ) );
psky.setBrush( NoBrush );
int pxperdegree = int(Options::zoomFactor()/57.3);
//fprintf(stderr, "in draw telescope function with mgrsize of %d\n", devMenu->mgr.size());
for (uint i=0; i < devMenu->mgr.count(); i++)
{
for (uint j=0; j < devMenu->mgr.at(i)->indi_dev.count(); j++)
{
useAltAz = false;
useJ2000 = false;
// make sure the dev is on first
if (devMenu->mgr.at(i)->indi_dev.at(j)->isOn())
{
portConnect = devMenu->mgr.at(i)->indi_dev.at(j)->findProp("CONNECTION");
if (!portConnect)
return;
if (portConnect->state == PS_BUSY)
return;
eqNum = devMenu->mgr.at(i)->indi_dev.at(j)->findProp("EQUATORIAL_EOD_COORD");
if (eqNum == NULL)
{
eqNum = devMenu->mgr.at(i)->indi_dev.at(j)->findProp("EQUATORIAL_COORD");
if (eqNum == NULL)
{
eqNum = devMenu->mgr.at(i)->indi_dev.at(j)->findProp("HORIZONTAL_COORD");
if (eqNum == NULL) continue;
else
useAltAz = true;
}
else
useJ2000 = true;
}
// make sure it has RA and DEC properties
if ( eqNum)
{
//fprintf(stderr, "Looking for RA label\n");
if (useAltAz)
{
lp = eqNum->findElement("AZ");
if (!lp)
continue;
dms azDMS(lp->value);
lp = eqNum->findElement("ALT");
if (!lp)
continue;
dms altDMS(lp->value);
indi_sp.setAz(azDMS);
indi_sp.setAlt(altDMS);
}
else
{
lp = eqNum->findElement("RA");
if (!lp)
continue;
// express hours in degrees on the celestial sphere
dms raDMS(lp->value);
raDMS.setD ( raDMS.Degrees() * 15.0);
lp = eqNum->findElement("DEC");
if (!lp)
continue;
dms decDMS(lp->value);
//kdDebug() << "the KStars RA is " << raDMS.toHMSString() << endl;
//kdDebug() << "the KStars DEC is " << decDMS.toDMSString() << "\n****************" << endl;
indi_sp.setRA(raDMS);
indi_sp.setDec(decDMS);
if (useJ2000)
{
indi_sp.setRA0(raDMS);
indi_sp.setDec0(decDMS);
indi_sp.apparentCoord( (double) J2000, ksw->data()->ut().djd());
}
if ( Options::useAltAz() ) indi_sp.EquatorialToHorizontal( ksw->LST(), ksw->geo()->lat() );
}
TQPoint P = getXY( &indi_sp, Options::useAltAz(), Options::useRefraction() );
int s1 = pxperdegree/2;
int s2 = pxperdegree;
int s3 = 2*pxperdegree;
int x0 = P.x(); int y0 = P.y();
int x1 = x0 - s1/2; int y1 = y0 - s1/2;
int x2 = x0 - s2/2; int y2 = y0 - s2/2;
int x3 = x0 - s3/2; int y3 = y0 - s3/2;
//Draw radial lines
psky.drawLine( x1, y0, x3, y0 );
psky.drawLine( x0+s2, y0, x0+s1/2, y0 );
psky.drawLine( x0, y1, x0, y3 );
psky.drawLine( x0, y0+s1/2, x0, y0+s2 );
//Draw circles at 0.5 & 1 degrees
psky.drawEllipse( x1, y1, s1, s1 );
psky.drawEllipse( x2, y2, s2, s2 );
psky.drawText( x0+s2 + 2 , y0, TQString(devMenu->mgr.at(i)->indi_dev.at(j)->label) );
}
}
}
}
}
}
void SkyMap::drawMilkyWay( TQPainter& psky, double scale )
{
int ptsCount = 0;
int mwmax = int( scale * Options::zoomFactor()/100.);
int Width = int( scale * width() );
int Height = int( scale * height() );
int thick(1);
if ( ! Options::fillMilkyWay() ) thick=3;
psky.setPen( TQPen( TQColor( data->colorScheme()->colorNamed( "MWColor" ) ), thick, SolidLine ) );
psky.setBrush( TQBrush( TQColor( data->colorScheme()->colorNamed( "MWColor" ) ) ) );
bool offscreen, lastoffscreen=false;
for ( unsigned int j=0; j<11; ++j ) {
if ( Options::fillMilkyWay() ) {
ptsCount = 0;
bool partVisible = false;
TQPoint o = getXY( data->MilkyWay[j].at(0), Options::useAltAz(), Options::useRefraction(), scale );
if ( o.x() != -10000000 && o.y() != -10000000 ) pts->setPoint( ptsCount++, o.x(), o.y() );
if ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) partVisible = true;
for ( SkyPoint *p = data->MilkyWay[j].first(); p; p = data->MilkyWay[j].next() ) {
o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
if ( o.x() != -10000000 && o.y() != -10000000 ) pts->setPoint( ptsCount++, o.x(), o.y() );
if ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) partVisible = true;
}
if ( ptsCount && partVisible ) {
psky.drawPolygon( ( const TQPointArray ) *pts, false, 0, ptsCount );
}
} else {
TQPoint o = getXY( data->MilkyWay[j].at(0), Options::useAltAz(), Options::useRefraction(), scale );
if (o.x()==-10000000 && o.y()==-10000000) offscreen = true;
else offscreen = false;
psky.moveTo( o.x(), o.y() );
for ( unsigned int i=1; i<data->MilkyWay[j].count(); ++i ) {
o = getXY( data->MilkyWay[j].at(i), Options::useAltAz(), Options::useRefraction(), scale );
if (o.x()==-10000000 && o.y()==-10000000) offscreen = true;
else offscreen = false;
//don't draw a line if the last point's getXY was (-10000000, -10000000)
int dx = abs(o.x()-psky.pos().x());
int dy = abs(o.y()-psky.pos().y());
if ( (!lastoffscreen && !offscreen) && (dx<mwmax && dy<mwmax) ) {
psky.lineTo( o.x(), o.y() );
} else {
psky.moveTo( o.x(), o.y() );
}
lastoffscreen = offscreen;
}
}
}
}
void SkyMap::drawCoordinateGrid( TQPainter& psky, double scale )
{
TQPoint cur;
//Draw coordinate grid
psky.setPen( TQPen( TQColor( data->colorScheme()->colorNamed( "GridColor" ) ), 1, DotLine ) ); //change to GridColor
//First, the parallels
for ( double Dec=-80.; Dec<=80.; Dec += 20. ) {
bool newlyVisible = false;
sp->set( 0.0, Dec );
if ( Options::useAltAz() ) sp->EquatorialToHorizontal( data->LST, data->geo()->lat() );
TQPoint o = getXY( sp, Options::useAltAz(), Options::useRefraction(), scale );
TQPoint o1 = o;
cur = o;
psky.moveTo( o.x(), o.y() );
double dRA = 1./5.; //120 points along full circle of RA
for ( double RA=dRA; RA<24.; RA+=dRA ) {
sp->set( RA, Dec );
if ( Options::useAltAz() ) sp->EquatorialToHorizontal( data->LST, data->geo()->lat() );
if ( checkVisibility( sp, guideFOV, guideXRange ) ) {
o = getXY( sp, Options::useAltAz(), Options::useRefraction(), scale );
//When drawing on the printer, the psky.pos() point does NOT get updated
//when lineTo or moveTo are called. Grrr. Need to store current position in TQPoint cur.
int dx = cur.x() - o.x();
int dy = cur.y() - o.y();
cur = o;
if ( abs(dx) < guidemax*scale && abs(dy) < guidemax*scale ) {
if ( newlyVisible ) {
newlyVisible = false;
psky.moveTo( o.x(), o.y() );
} else {
psky.lineTo( o.x(), o.y() );
}
} else {
psky.moveTo( o.x(), o.y() );
}
}
}
//connect the final segment
int dx = psky.pos().x() - o1.x();
int dy = psky.pos().y() - o1.y();
if ( abs(dx) < guidemax*scale && abs(dy) < guidemax*scale ) {
psky.lineTo( o1.x(), o1.y() );
} else {
psky.moveTo( o1.x(), o1.y() );
}
}
//next, the meridians
for ( double RA=0.; RA<24.; RA += 2. ) {
bool newlyVisible = false;
SkyPoint *sp1 = new SkyPoint( RA, -90. );
if ( Options::useAltAz() ) sp1->EquatorialToHorizontal( data->LST, data->geo()->lat() );
TQPoint o = getXY( sp1, Options::useAltAz(), Options::useRefraction(), scale );
cur = o;
psky.moveTo( o.x(), o.y() );
double dDec = 1.;
for ( double Dec=-89.; Dec<=90.; Dec+=dDec ) {
sp1->set( RA, Dec );
if ( Options::useAltAz() ) sp1->EquatorialToHorizontal( data->LST, data->geo()->lat() );
if ( checkVisibility( sp1, guideFOV, guideXRange ) ) {
o = getXY( sp1, Options::useAltAz(), Options::useRefraction(), scale );
//When drawing on the printer, the psky.pos() point does NOT get updated
//when lineTo or moveTo are called. Grrr. Need to store current position in TQPoint cur.
int dx = cur.x() - o.x();
int dy = cur.y() - o.y();
cur = o;
if ( abs(dx) < guidemax*scale && abs(dy) < guidemax*scale ) {
if ( newlyVisible ) {
newlyVisible = false;
psky.moveTo( o.x(), o.y() );
} else {
psky.lineTo( o.x(), o.y() );
}
} else {
psky.moveTo( o.x(), o.y() );
}
}
}
delete sp1; // avoid memory leak
}
}
void SkyMap::drawEquator( TQPainter& psky, double scale )
{
//Draw Equator (currently can't be hidden on slew)
psky.setPen( TQPen( TQColor( data->colorScheme()->colorNamed( "EqColor" ) ), 1, SolidLine ) );
SkyPoint *p = data->Equator.first();
TQPoint o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
TQPoint o1 = o;
TQPoint last = o;
TQPoint cur = o;
TQPoint o2;
psky.moveTo( o.x(), o.y() );
bool newlyVisible = false;
//index of point near the left or top/bottom edge
uint index1(0), index2(0);
int xSmall(width() + 100); //ridiculous initial value
//start loop at second item
for ( p = data->Equator.next(); p; p = data->Equator.next() ) {
if ( checkVisibility( p, guideFOV, guideXRange ) ) {
o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
//first iteration for positioning the "Equator" label:
//flag the onscreen equator point with the smallest positive x value
//we don't draw the label while slewing
if ( ! slewing && o.x() > 0 && o.x() < width() && o.y() > 0 && o.y() < height() ) {
if ( o.x() < xSmall ) {
xSmall = o.x();
index1 = data->Equator.at();
}
}
//When drawing on the printer, the psky.pos() point does NOT get updated
//when lineTo or moveTo are called. Grrr. Need to store current position in TQPoint cur.
int dx = cur.x() - o.x();
int dy = cur.y() - o.y();
cur = o;
if ( abs(dx) < guidemax*scale && abs(dy) < guidemax*scale ) {
if ( newlyVisible ) {
newlyVisible = false;
psky.moveTo( last.x(), last.y() );
}
psky.lineTo( o.x(), o.y() );
} else {
psky.moveTo( o.x(), o.y() );
}
} else {
newlyVisible = true;
}
last = o;
}
//connect the final segment
int dx = psky.pos().x() - o1.x();
int dy = psky.pos().y() - o1.y();
if ( abs(dx) < guidemax*scale && abs(dy) < guidemax*scale ) {
psky.lineTo( o1.x(), o1.y() );
} else {
psky.moveTo( o1.x(), o1.y() );
}
if ( ! slewing && xSmall < width() ) {
//Draw the "Equator" label. We have flagged the leftmost onscreen Equator point.
//If the zoom level is below 1000, simply adopt this point as the anchor for the
//label. If the zoom level is 1000 or higher, we interpolate to find the exact
//point at which the Equator goes offscreen, and anchor from that point.
p = data->Equator.at(index1);
double ra0(0.0); //the RA of our anchor point (the Dec is known to be 0.0
//since it's the Equator)
if ( Options::zoomFactor() < 1000. ) {
ra0 = p->ra()->Hours();
} else {
//Somewhere between Equator point p and its immediate neighbor, the Equator goes
//offscreen. Determine the exact point at which this happens.
index2 = index1 + 1;
if ( index2 >= data->Equator.count() ) index2 -= data->Equator.count();
SkyPoint *p2 = data->Equator.at(index2);
o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
o2 = getXY( p2, Options::useAltAz(), Options::useRefraction(), scale );
double x1, x2;
//there are 3 possibilities: (o2.x() < 0); (o2.y() < 0); (o2.y() > height())
if ( o2.x() < 0 ) {
x1 = double(o.x())/double(o.x()-o2.x());
x2 = -1.0*double(o2.x())/double(o.x()-o2.x());
} else if ( o2.y() < 0 ) {
x1 = double(o.y())/double(o.y()-o2.y());
x2 = -1.0*double(o2.y())/double(o.y()-o2.y());
} else if ( o2.y() > height() ) {
x1 = double(height() - o.y())/double(o2.y()-o.y());
x2 = double(o2.y() - height())/double(o2.y()-o.y());
} else { //should never get here
x1 = 0.0;
x2 = 1.0;
}
//ra0 is the exact RA at which the Equator intersects a screen edge
ra0 = x1*p2->ra()->Hours() + x2*p->ra()->Hours();
}
//LabelPoint is the top left corner of the text label. It is
//offset from the anchor point by -1.5 degree (0.1 hour) in RA
//and -0.4 degree in Dec, scaled by 2000./zoomFactor so that they are
//independent of zoom.
SkyPoint LabelPoint( ra0 - 200./Options::zoomFactor(), -800./Options::zoomFactor() );
if ( Options::useAltAz() )
LabelPoint.EquatorialToHorizontal( data->LST, data->geo()->lat() );
//p2 is a SkyPoint offset from LabelPoint in RA by -0.1 hour/zoomFactor.
//We use this point to determine the rotation angle for the text (which
//we want to be parallel to the line joining LabelPoint and p2)
SkyPoint p2 = LabelPoint;
p2.setRA( p2.ra()->Hours() - 200./Options::zoomFactor() );
if ( Options::useAltAz() )
p2.EquatorialToHorizontal( data->LST, data->geo()->lat() );
//o and o2 are the screen coordinates of LabelPoint and p2.
o = getXY( &LabelPoint, Options::useAltAz(), Options::useRefraction(), scale );
o2 = getXY( &p2, Options::useAltAz(), Options::useRefraction() );
double sx = double( o.x() - o2.x() );
double sy = double( o.y() - o2.y() );
double angle;
if ( sx ) {
angle = atan( sy/sx )*180.0/dms::PI;
} else {
angle = 90.0;
if ( sy < 0 ) angle = -90.0;
}
//Finally, draw the "Equator" label at the determined location and angle
psky.save();
psky.translate( o.x(), o.y() );
psky.rotate( double( angle ) ); //rotate the coordinate system
psky.drawText( 0, 0, i18n( "Equator" ) );
psky.restore(); //reset coordinate system
}
}
void SkyMap::drawEcliptic( TQPainter& psky, double scale )
{
int Width = int( scale * width() );
int Height = int( scale * height() );
//Draw Ecliptic (currently can't be hidden on slew)
psky.setPen( TQPen( TQColor( data->colorScheme()->colorNamed( "EclColor" ) ), 1, SolidLine ) );
SkyPoint *p = data->Ecliptic.first();
TQPoint o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
TQPoint o2 = o;
TQPoint o1 = o;
TQPoint last = o;
TQPoint cur = o;
psky.moveTo( o.x(), o.y() );
//index of point near the right or top/bottom edge
uint index1(0), index2(0);
int xBig(-100); //ridiculous initial value
bool newlyVisible = false;
//Start loop at second item
for ( p = data->Ecliptic.next(); p; p = data->Ecliptic.next() ) {
if ( checkVisibility( p, guideFOV, guideXRange ) ) {
o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
//first iteration for positioning the "Ecliptic" label:
//flag the onscreen equator point with the largest x value
//we don't draw the label while slewing
if ( ! slewing && o.x() > 0 && o.x() < width() && o.y() > 0 && o.y() < height() ) {
if ( o.x() > xBig ) {
xBig = o.x();
index1 = data->Ecliptic.at();
}
}
//When drawing on the printer, the psky.pos() point does NOT get updated
//when lineTo or moveTo are called. Grrr. Need to store current position in TQPoint cur.
int dx = cur.x() - o.x();
int dy = cur.y() - o.y();
cur = o;
if ( abs(dx) < guidemax*scale && abs(dy) < guidemax*scale ) {
if ( newlyVisible ) {
newlyVisible = false;
psky.moveTo( last.x(), last.y() );
}
psky.lineTo( o.x(), o.y() );
} else {
psky.moveTo( o.x(), o.y() );
}
} else {
newlyVisible = true;
}
last = o;
}
//connect the final segment
int dx = psky.pos().x() - o1.x();
int dy = psky.pos().y() - o1.y();
if ( abs(dx) < Width && abs(dy) < Height ) {
psky.lineTo( o1.x(), o1.y() );
} else {
psky.moveTo( o1.x(), o1.y() );
}
if ( ! slewing && xBig > 0 ) {
//Draw the "Ecliptic" label. We have flagged the rightmost onscreen Ecliptic point.
//If the zoom level is below 1000, simply adopt this point as the anchor for the
//label. If the zoom level is 1000 or higher, we interpolate to find the exact
//point at which the Ecliptic goes offscreen, and anchor from that point.
p = data->Ecliptic.at(index1);
double ra0(0.0); //the ra of our anchor point
double dec0(0.0); //the dec of our anchor point
if ( Options::zoomFactor() < 1000. ) {
ra0 = p->ra()->Hours();
dec0 = p->dec()->Degrees();
} else {
//Somewhere between Ecliptic point p and its immediate neighbor, the Ecliptic goes
//offscreen. Determine the exact point at which this happens.
if ( index1 == 0 ) index2 = 0;
else index2 = index1 - 1;
SkyPoint *p2 = data->Ecliptic.at(index2);
o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
o2 = getXY( p2, Options::useAltAz(), Options::useRefraction(), scale );
double x1, x2;
//there are 3 possibilities: (o2.x() > width()); (o2.y() < 0); (o2.y() > height())
if ( o2.x() > width() ) {
x1 = double(width()-o.x())/double(o2.x()-o.x());
x2 = double(o2.x()-width())/double(o2.x()-o.x());
} else if ( o2.y() < 0 ) {
x1 = double(o.y())/double(o.y()-o2.y());
x2 = -1.0*double(o2.y())/double(o.y()-o2.y());
} else if ( o2.y() > height() ) {
x1 = double(height() - o.y())/double(o2.y()-o.y());
x2 = double(o2.y() - height())/double(o2.y()-o.y());
} else { //should never get here
x1 = 0.0;
x2 = 1.0;
}
//ra0 is the exact RA at which the Ecliptic intersects a screen edge
ra0 = x1*p2->ra()->Hours() + x2*p->ra()->Hours();
//dec0 is the exact Dec at which the Ecliptic intersects a screen edge
dec0 = x1*p2->dec()->Degrees() + x2*p->dec()->Degrees();
}
KSNumbers num( data->ut().djd() );
dms ecLong, ecLat;
//LabelPoint is offset from the anchor point by +2.0 degree ecl. Long
//and -0.4 degree in ecl. Lat, scaled by 2000./zoomFactor so that they are
//independent of zoom.
SkyPoint LabelPoint(ra0, dec0);
LabelPoint.findEcliptic( num.obliquity(), ecLong, ecLat );
ecLong.setD( ecLong.Degrees() + 4000./Options::zoomFactor() );
ecLat.setD( ecLat.Degrees() - 800./Options::zoomFactor() );
LabelPoint.setFromEcliptic( num.obliquity(), &ecLong, &ecLat );
if ( Options::useAltAz() )
LabelPoint.EquatorialToHorizontal( data->LST, data->geo()->lat() );
//p2 is a SkyPoint offset from LabelPoint by -1.0 degrees of ecliptic longitude.
//we use p2 to determine the onscreen rotation angle for the ecliptic label,
//which we want to be parallel to the line between LabelPoint and p2.
SkyPoint p2(ra0, dec0);
p2.findEcliptic( num.obliquity(), ecLong, ecLat );
ecLong.setD( ecLong.Degrees() + 2000./Options::zoomFactor() );
ecLat.setD( ecLat.Degrees() - 800./Options::zoomFactor() );
p2.setFromEcliptic( num.obliquity(), &ecLong, &ecLat );
if ( Options::useAltAz() )
p2.EquatorialToHorizontal( data->LST, data->geo()->lat() );
//o and o2 are the screen positions of LabelPoint and p2.
o = getXY( &LabelPoint, Options::useAltAz(), Options::useRefraction(), scale );
o2 = getXY( &p2, Options::useAltAz(), Options::useRefraction() );
double sx = double( o.x() - o2.x() );
double sy = double( o.y() - o2.y() );
double angle;
if ( sx ) {
angle = atan( sy/sx )*180.0/dms::PI;
} else {
angle = 90.0;
if ( sy < 0 ) angle = -90.0;
}
//Finally, draw the "Ecliptic" label at the determined location and angle
psky.save();
psky.translate( o.x(), o.y() );
psky.rotate( double( angle ) ); //rotate the coordinate system
psky.drawText( 0, 0, i18n( "Ecliptic" ) );
psky.restore(); //reset coordinate system
}
}
void SkyMap::drawHorizon( TQPainter& psky, double scale )
{
int Width = int( scale * width() );
int Height = int( scale * height() );
TQPtrList<TQPoint> points;
points.setAutoDelete(true);
TQPoint o, o2;
//Draw Horizon
//The horizon should not be corrected for atmospheric refraction, so getXY has doRefract=false...
if (Options::showHorizon() || Options::showGround() ) {
TQPoint OutLeft(0,0), OutRight(0,0);
psky.setPen( TQPen( TQColor( data->colorScheme()->colorNamed( "HorzColor" ) ), 1, SolidLine ) );
psky.setBrush( TQColor ( data->colorScheme()->colorNamed( "HorzColor" ) ) );
int ptsCount = 0;
int maxdist = int(Options::zoomFactor()/4);
//index of point near the right or top/bottom edge
uint index1(0), index2(0);
int xBig(-100); //ridiculous initial value
for ( SkyPoint *p = data->Horizon.first(); p; p = data->Horizon.next() ) {
o = getXY( p, Options::useAltAz(), false, scale ); //false: do not refract the horizon
bool found = false;
//first iteration for positioning the "Horizon" label:
//flag the onscreen equator point with the largest x value
//we don't draw the label while slewing, or if the opaque ground is drawn
if ( ! slewing && ( ! Options::showGround() || ! Options::useAltAz() )
&& o.x() > 0 && o.x() < width() && o.y() > 0 && o.y() < height() ) {
if ( o.x() > xBig ) {
xBig = o.x();
index1 = data->Horizon.at();
}
}
//Use the TQPtrList of points to pre-sort visible horizon points
if ( o.x() > -100 && o.x() < Width + 100 && o.y() > -100 && o.y() < Height + 100 ) {
if ( Options::useAltAz() ) {
unsigned int j;
for ( j=0; j<points.count(); ++j ) {
if ( o.x() < points.at(j)->x() ) {
found = true;
break;
}
}
if ( found ) {
points.insert( j, new TQPoint(o) );
} else {
points.append( new TQPoint(o) );
}
} else {
points.append( new TQPoint(o) );
}
} else { //find the out-of-bounds points closest to the left and right borders
if ( ( OutLeft.x() == 0 || o.x() > OutLeft.x() ) && o.x() < -100 ) {
OutLeft.setX( o.x() );
OutLeft.setY( o.y() );
}
if ( ( OutRight.x() == 0 || o.x() < OutRight.x() ) && o.x() > + 100 ) {
OutRight.setX( o.x() );
OutRight.setY( o.y() );
}
}
}
//Add left-edge and right-edge points based on interpolating the first/last onscreen points
//to the nearest offscreen points.
if ( Options::useAltAz() && points.count() > 0 ) {
//If the edge of the visible sky circle is onscreen, then we should
//interpolate the first and last horizon points to the edge of the circle.
//Otherwise, interpolate to the screen edge.
double dx = 0.5*double(Width)/Options::zoomFactor(); //center-to-edge ang in radians
double r0 = 2.0*sin(0.25*dms::PI);
if ( dx < r0 ) { //edge of visible sky circle is not visible
//Interpolate from first sorted onscreen point to x=-100,
//using OutLeft to determine the slope
int xtotal = ( points.at( 0 )->x() - OutLeft.x() );
int xx = ( points.at( 0 )->x() + 100 ) / xtotal;
int yp = xx*OutRight.y() + (1-xx)*points.at( 0 )->y(); //interpolated left-edge y value
TQPoint *LeftEdge = new TQPoint( -100, yp );
points.insert( 0, LeftEdge ); //Prepend LeftEdge to the beginning of points
//Interpolate from the last sorted onscreen point to ()+100,
//using OutRight to determine the slope.
xtotal = ( OutRight.x() - points.at( points.count() - 1 )->x() );
xx = ( Width + 100 - points.at( points.count() - 1 )->x() ) / xtotal;
yp = xx*OutRight.y() + (1-xx)*points.at( points.count() - 1 )->y(); //interpolated right-edge y value
TQPoint *RightEdge = new TQPoint( Width+100, yp );
points.append( RightEdge );
}
//If there are no horizon points, then either the horizon doesn't pass through the screen
//or we're at high zoom, and horizon points lie on either side of the screen.
} else if ( Options::useAltAz() && OutLeft.y() !=0 && OutRight.y() !=0 &&
!( OutLeft.y() > Height + 100 && OutRight.y() > Height + 100 ) &&
!( OutLeft.y() < -100 && OutRight.y() < -100 ) ) {
//It's possible at high zoom that /no/ horizon points are onscreen. In this case,
//interpolate between OutLeft and OutRight directly to construct the horizon polygon.
int xtotal = ( OutRight.x() - OutLeft.x() );
int xx = ( OutRight.x() + 100 ) / xtotal;
int yp = xx*OutLeft.y() + (1-xx)*OutRight.y(); //interpolated left-edge y value
// TQPoint *LeftEdge = new TQPoint( -100, yp );
points.append( new TQPoint( -100, yp ) );
xx = ( Width + 100 - OutLeft.x() ) / xtotal;
yp = xx*OutRight.y() + (1-xx)*OutLeft.y(); //interpolated right-edge y value
// TQPoint *RightEdge = new TQPoint( Width+100, yp );
points.append( new TQPoint( Width+100, yp ) );
}
if ( points.count() ) {
// Fill the pts array with sorted horizon points, Draw Horizon Line
pts->setPoint( 0, points.at(0)->x(), points.at(0)->y() );
if ( Options::showHorizon() ) psky.moveTo( points.at(0)->x(), points.at(0)->y() );
for ( unsigned int i=1; i<points.count(); ++i ) {
pts->setPoint( i, points.at(i)->x(), points.at(i)->y() );
if ( Options::showHorizon() ) {
if ( !Options::useAltAz() && ( abs( points.at(i)->x() - psky.pos().x() ) > maxdist ||
abs( points.at(i)->y() - psky.pos().y() ) > maxdist ) ) {
psky.moveTo( points.at(i)->x(), points.at(i)->y() );
} else {
psky.lineTo( points.at(i)->x(), points.at(i)->y() );
}
}
}
//connect the last segment back to the beginning
if ( abs( points.at(0)->x() - psky.pos().x() ) < maxdist && abs( points.at(0)->y() - psky.pos().y() ) < maxdist )
psky.lineTo( points.at(0)->x(), points.at(0)->y() );
//Finish the Ground polygon. If sky edge is visible, the
//bottom edge follows the sky circle. Otherwise, we just
//add a square bottom edge, offscreen
if ( Options::useAltAz() ) {
if ( Options::showGround() ) {
ptsCount = points.count();
//center-to-edge ang in radians
double dx = 0.5*double(Width)/Options::zoomFactor();
double r0 = 2.0*sin(0.25*dms::PI);
// //Second TQPointsArray for blocking the region outside the sky circle
// TQPointArray bpts( 100 ); //need 90 points along sky circle, plus 4 to complete polygon
// uint bpCount(0);
if ( dx > r0 ) { //sky edge is visible
for ( double t=360.; t >= 180.; t-=2. ) { //add points along edge of circle
dms a( t );
double sa(0.), ca(0.);
a.SinCos( sa, ca );
int xx = Width/2 + int(r0*Options::zoomFactor()*ca);
int yy = Height/2 - int(r0*Options::zoomFactor()*sa);
pts->setPoint( ptsCount++, xx, yy );
// bpts.setPoint( bpCount++, xx, yy );
}
// //complete the background polygon, then draw it with SkyColor
// bpts.setPoint( bpCount++, -100, Height/2 );
// bpts.setPoint( bpCount++, -100, Height + 100 );
// bpts.setPoint( bpCount++, Width + 100, Height + 100 );
// bpts.setPoint( bpCount++, Width + 100, Height/2 );
// psky.setPen( TQColor ( data->colorScheme()->colorNamed( "SkyColor" ) ) );
// psky.setBrush( TQColor ( data->colorScheme()->colorNamed( "SkyColor" ) ) );
// psky.drawPolygon( bpts, false, 0, bpCount );
// //Reset colors for Horizon polygon
// psky.setPen( TQColor ( data->colorScheme()->colorNamed( "HorzColor" ) ) );
// psky.setBrush( TQColor ( data->colorScheme()->colorNamed( "HorzColor" ) ) );
} else {
pts->setPoint( ptsCount++, Width+100, Height+100 ); //bottom right corner
pts->setPoint( ptsCount++, -100, Height+100 ); //bottom left corner
}
//Draw the Horizon polygon
psky.drawPolygon( ( const TQPointArray ) *pts, false, 0, ptsCount );
//remove all items in points list
for ( unsigned int i=0; i<points.count(); ++i ) {
points.remove(i);
}
}
// Draw compass heading labels along horizon
SkyPoint *c = new SkyPoint;
TQPoint cpoint;
if ( Options::showGround() )
psky.setPen( TQColor ( data->colorScheme()->colorNamed( "CompassColor" ) ) );
else
psky.setPen( TQColor ( data->colorScheme()->colorNamed( "HorzColor" ) ) );
//North
c->setAz( 359.99 );
c->setAlt( 0.0 );
if ( !Options::useAltAz() ) c->HorizontalToEquatorial( data->LST, data->geo()->lat() );
cpoint = getXY( c, Options::useAltAz(), false, scale );
cpoint.setY( cpoint.y() + int(scale*20) );
if (cpoint.x() > 0 && cpoint.x() < Width && cpoint.y() > 0 && cpoint.y() < Height ) {
psky.drawText( cpoint.x(), cpoint.y(), i18n( "North", "N" ) );
}
//NorthEast
c->setAz( 45.0 );
c->setAlt( 0.0 );
if ( !Options::useAltAz() ) c->HorizontalToEquatorial( data->LST, data->geo()->lat() );
cpoint = getXY( c, Options::useAltAz(), false, scale );
cpoint.setY( cpoint.y() + int(scale*20) );
if (cpoint.x() > 0 && cpoint.x() < Width && cpoint.y() > 0 && cpoint.y() < Height ) {
psky.drawText( cpoint.x(), cpoint.y(), i18n( "Northeast", "NE" ) );
}
//East
c->setAz( 90.0 );
c->setAlt( 0.0 );
if ( !Options::useAltAz() ) c->HorizontalToEquatorial( data->LST, data->geo()->lat() );
cpoint = getXY( c, Options::useAltAz(), false, scale );
cpoint.setY( cpoint.y() + int(scale*20) );
if (cpoint.x() > 0 && cpoint.x() < Width && cpoint.y() > 0 && cpoint.y() < Height ) {
psky.drawText( cpoint.x(), cpoint.y(), i18n( "East", "E" ) );
}
//SouthEast
c->setAz( 135.0 );
c->setAlt( 0.0 );
if ( !Options::useAltAz() ) c->HorizontalToEquatorial( data->LST, data->geo()->lat() );
cpoint = getXY( c, Options::useAltAz(), false, scale );
cpoint.setY( cpoint.y() + int(scale*20) );
if (cpoint.x() > 0 && cpoint.x() < Width && cpoint.y() > 0 && cpoint.y() < Height ) {
psky.drawText( cpoint.x(), cpoint.y(), i18n( "Southeast", "SE" ) );
}
//South
c->setAz( 179.99 );
c->setAlt( 0.0 );
if ( !Options::useAltAz() ) c->HorizontalToEquatorial( data->LST, data->geo()->lat() );
cpoint = getXY( c, Options::useAltAz(), false, scale );
cpoint.setY( cpoint.y() + int(scale*20) );
if (cpoint.x() > 0 && cpoint.x() < Width && cpoint.y() > 0 && cpoint.y() < Height ) {
psky.drawText( cpoint.x(), cpoint.y(), i18n( "South", "S" ) );
}
//SouthWest
c->setAz( 225.0 );
c->setAlt( 0.0 );
if ( !Options::useAltAz() ) c->HorizontalToEquatorial( data->LST, data->geo()->lat() );
cpoint = getXY( c, Options::useAltAz(), false, scale );
cpoint.setY( cpoint.y() + int(scale*20) );
if (cpoint.x() > 0 && cpoint.x() < Width && cpoint.y() > 0 && cpoint.y() < Height ) {
psky.drawText( cpoint.x(), cpoint.y(), i18n( "Southwest", "SW" ) );
}
//West
c->setAz( 270.0 );
c->setAlt( 0.0 );
if ( !Options::useAltAz() ) c->HorizontalToEquatorial( data->LST, data->geo()->lat() );
cpoint = getXY( c, Options::useAltAz(), false, scale );
cpoint.setY( cpoint.y() + int(scale*20) );
if (cpoint.x() > 0 && cpoint.x() < Width && cpoint.y() > 0 && cpoint.y() < Height ) {
psky.drawText( cpoint.x(), cpoint.y(), i18n( "West", "W" ) );
}
//NorthWest
c->setAz( 315.0 );
c->setAlt( 0.0 );
if ( !Options::useAltAz() ) c->HorizontalToEquatorial( data->LST, data->geo()->lat() );
cpoint = getXY( c, Options::useAltAz(), false, scale );
cpoint.setY( cpoint.y() + int(scale*20) );
if (cpoint.x() > 0 && cpoint.x() < Width && cpoint.y() > 0 && cpoint.y() < Height ) {
psky.drawText( cpoint.x(), cpoint.y(), i18n( "Northwest", "NW" ) );
}
delete c;
}
}
if ( ! slewing && (! Options::showGround() || ! Options::useAltAz() ) && xBig > 0 ) {
//Draw the "Horizon" label. We have flagged the rightmost onscreen Horizon point.
//If the zoom level is below 1000, simply adopt this point as the anchor for the
//label. If the zoom level is 1000 or higher, we interpolate to find the exact
//point at which the Horizon goes offscreen, and anchor from that point.
SkyPoint *p = data->Horizon.at(index1);
double ra0(0.0); //the ra of our anchor point
double dec0(0.0); //the dec of our anchor point
if ( Options::zoomFactor() < 1000. ) {
ra0 = p->ra()->Hours();
dec0 = p->dec()->Degrees();
} else {
//Somewhere between Horizon point p and its immediate neighbor, the Horizon goes
//offscreen. Determine the exact point at which this happens.
index2 = index1 + 1;
if ( data->Horizon.count() && index2 > data->Horizon.count() - 1 ) index2 -= data->Horizon.count();
SkyPoint *p2 = data->Horizon.at(index2);
TQPoint o1 = getXY( p, Options::useAltAz(), false, scale );
TQPoint o2 = getXY( p2, Options::useAltAz(), false, scale );
double x1, x2;
//there are 3 possibilities: (o2.x() > width()); (o2.y() < 0); (o2.y() > height())
if ( o2.x() > width() ) {
x1 = double(width()-o1.x())/double(o2.x()-o1.x());
x2 = double(o2.x()-width())/double(o2.x()-o1.x());
} else if ( o2.y() < 0 ) {
x1 = double(o1.y())/double(o1.y()-o2.y());
x2 = -1.0*double(o2.y())/double(o1.y()-o2.y());
} else if ( o2.y() > height() ) {
x1 = double(height() - o1.y())/double(o2.y()-o1.y());
x2 = double(o2.y() - height())/double(o2.y()-o1.y());
} else { //should never get here
x1 = 0.0;
x2 = 1.0;
}
//ra0 is the exact RA at which the Horizon intersects a screen edge
ra0 = x1*p2->ra()->Hours() + x2*p->ra()->Hours();
//dec0 is the exact Dec at which the Horizon intersects a screen edge
dec0 = x1*p2->dec()->Degrees() + x2*p->dec()->Degrees();
}
//LabelPoint is offset from the anchor point by -2.0 degrees in azimuth
//and -0.4 degree altitude, scaled by 2000./zoomFactor so that they are
//independent of zoom.
SkyPoint LabelPoint(ra0, dec0);
LabelPoint.EquatorialToHorizontal( data->LST, data->geo()->lat() );
LabelPoint.setAlt( LabelPoint.alt()->Degrees() - 800./Options::zoomFactor() );
LabelPoint.setAz( LabelPoint.az()->Degrees() - 4000./Options::zoomFactor() );
LabelPoint.HorizontalToEquatorial( data->LST, data->geo()->lat() );
o = getXY( &LabelPoint, Options::useAltAz(), false, scale );
if ( o.x() > width() || o.x() < 0 ) {
//the LabelPoint is offscreen. Either we are in the Southern hemisphere,
//or the sky is rotated upside-down. Use an azimuth offset of +2.0 degrees
LabelPoint.setAlt( LabelPoint.alt()->Degrees() + 1600./Options::zoomFactor() );
LabelPoint.setAz( LabelPoint.az()->Degrees() + 8000./Options::zoomFactor() );
LabelPoint.HorizontalToEquatorial( data->LST, data->geo()->lat() );
o = getXY( &LabelPoint, Options::useAltAz(), false, scale );
}
//p2 is a skypoint offset from LabelPoint by +/-1 degree azimuth (scaled by
//2000./zoomFactor). We use p2 to determine the rotation angle for the
//Horizon label, which we want to be parallel to the line between LabelPoint and p2.
SkyPoint p2( LabelPoint.ra(), LabelPoint.dec() );
p2.EquatorialToHorizontal( data->LST, data->geo()->lat() );
p2.setAz( p2.az()->Degrees() + 2000./Options::zoomFactor() );
p2.HorizontalToEquatorial( data->LST, data->geo()->lat() );
//o and o2 are the screen positions of LabelPoint and p2
o = getXY( &LabelPoint, Options::useAltAz(), false, scale );
o2 = getXY( &p2, Options::useAltAz(), false, scale );
double sx = double( o.x() - o2.x() );
double sy = double( o.y() - o2.y() );
double angle;
if ( sx ) {
angle = atan( sy/sx )*180.0/dms::PI;
} else {
angle = 90.0;
if ( sy < 0 ) angle = -90.0;
}
//Finally, draw the "Equator" label at the determined location and angle
psky.save();
psky.translate( o.x(), o.y() );
psky.rotate( double( angle ) ); //rotate the coordinate system
psky.drawText( 0, 0, i18n( "Horizon" ) );
psky.restore(); //reset coordinate system
}
} //endif drawing horizon
}
void SkyMap::drawConstellationLines( TQPainter& psky, double scale )
{
int Width = int( scale * width() );
int Height = int( scale * height() );
//Draw Constellation Lines
psky.setPen( TQPen( TQColor( data->colorScheme()->colorNamed( "CLineColor" ) ), 1, SolidLine ) ); //change to colorGrid
int iLast = -1;
for ( SkyPoint *p = data->clineList.first(); p; p = data->clineList.next() ) {
TQPoint o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
if ( ( o.x() >= -1000 && o.x() <= Width+1000 && o.y() >=-1000 && o.y() <= Height+1000 ) ) {
if ( data->clineModeList.at(data->clineList.at())->latin1()=='M' ) {
psky.moveTo( o.x(), o.y() );
} else if ( data->clineModeList.at(data->clineList.at())->latin1()=='D' ) {
if ( data->clineList.at()== (int)(iLast+1) ) {
psky.lineTo( o.x(), o.y() );
} else {
psky.moveTo( o.x(), o.y() );
}
}
iLast = data->clineList.at();
}
}
}
void SkyMap::drawConstellationBoundaries( TQPainter &psky, double scale ) {
int Width = int( scale * width() );
int Height = int( scale * height() );
psky.setPen( TQPen( TQColor( data->colorScheme()->colorNamed( "CBoundColor" ) ), 1, SolidLine ) );
for ( CSegment *seg = data->csegmentList.first(); seg; seg = data->csegmentList.next() ) {
bool started( false );
SkyPoint *p = seg->firstNode();
TQPoint o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
if ( ( o.x() >= -1000 && o.x() <= Width+1000 && o.y() >=-1000 && o.y() <= Height+1000 ) ) {
psky.moveTo( o.x(), o.y() );
started = true;
}
for ( p = seg->nextNode(); p; p = seg->nextNode() ) {
TQPoint o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
if ( ( o.x() >= -1000 && o.x() <= Width+1000 && o.y() >=-1000 && o.y() <= Height+1000 ) ) {
if ( started ) {
psky.lineTo( o.x(), o.y() );
} else {
psky.moveTo( o.x(), o.y() );
started = true;
}
} else {
started = false;
}
}
}
}
void SkyMap::drawConstellationNames( TQPainter& psky, double scale ) {
int Width = int( scale * width() );
int Height = int( scale * height() );
//Draw Constellation Names
psky.setPen( TQColor( data->colorScheme()->colorNamed( "CNameColor" ) ) );
for ( SkyObject *p = data->cnameList.first(); p; p = data->cnameList.next() ) {
if ( checkVisibility( p, fov(), XRange ) ) {
TQPoint o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
if (o.x() >= 0 && o.x() <= Width && o.y() >=0 && o.y() <= Height ) {
if ( Options::useLatinConstellNames() ) {
int dx = 5*p->name().length();
psky.drawText( o.x()-dx, o.y(), p->name() ); // latin constellation names
} else if ( Options::useLocalConstellNames() ) {
// can't use translatedName() because we need the context string in i18n()
int dx = 5*( i18n( "Constellation name (optional)", p->name().local8Bit().data() ).length() );
psky.drawText( o.x()-dx, o.y(), i18n( "Constellation name (optional)", p->name().local8Bit().data() ) ); // localized constellation names
} else {
int dx = 5*p->name2().length();
psky.drawText( o.x()-dx, o.y(), p->name2() ); //name2 is the IAU abbreviation
}
}
}
}
}
void SkyMap::drawStars( TQPainter& psky, double scale ) {
int Width = int( scale * width() );
int Height = int( scale * height() );
bool checkSlewing = ( ( slewing || ( clockSlewing && data->clock()->isActive() ) )
&& Options::hideOnSlew() );
//shortcuts to inform wheter to draw different objects
bool hideFaintStars( checkSlewing && Options::hideStars() );
if ( Options::showStars() ) {
//adjust maglimit for ZoomLevel
double lgmin = log10(MINZOOM);
double lgmax = log10(MAXZOOM);
double lgz = log10(Options::zoomFactor());
double maglim = Options::magLimitDrawStar();
if ( lgz <= 0.75*lgmax ) maglim -= (Options::magLimitDrawStar() - Options::magLimitDrawStarZoomOut() )*(0.75*lgmax - lgz)/(0.75*lgmax - lgmin);
float sizeFactor = 6.0 + (lgz - lgmin);
for ( StarObject *curStar = data->starList.first(); curStar; curStar = data->starList.next() ) {
// break loop if maglim is reached
if ( curStar->mag() > maglim || ( hideFaintStars && curStar->mag() > Options::magLimitHideStar() ) ) break;
if ( checkVisibility( curStar, fov(), XRange ) ) {
TQPoint o = getXY( curStar, Options::useAltAz(), Options::useRefraction(), scale );
// draw star if currently on screen
if (o.x() >= 0 && o.x() <= Width && o.y() >=0 && o.y() <= Height ) {
int size = int( scale * ( sizeFactor*( maglim - curStar->mag())/maglim ) + 1 );
if ( size > 0 ) {
TQChar c = curStar->color();
TQPixmap *spixmap = starpix->getPixmap( &c, size );
curStar->draw( psky, sky, spixmap, o.x(), o.y(), true, scale );
// now that we have drawn the star, we can display some extra info
//don't label unnamed stars with the generic "star" name
bool drawName = ( Options::showStarNames() && (curStar->name() != i18n("star") ) );
if ( !checkSlewing && (curStar->mag() <= Options::magLimitDrawStarInfo() )
&& ( drawName || Options::showStarMagnitudes() ) ) {
psky.setPen( TQColor( data->colorScheme()->colorNamed( "SNameColor" ) ) );
TQFont stdFont( psky.font() );
TQFont smallFont( stdFont );
smallFont.setPointSize( stdFont.pointSize() - 2 );
if ( Options::zoomFactor() < 10.*MINZOOM ) {
psky.setFont( smallFont );
} else {
psky.setFont( stdFont );
}
curStar->drawLabel( psky, o.x(), o.y(), Options::zoomFactor(),
drawName, Options::showStarMagnitudes(), scale );
//reset font
psky.setFont( stdFont );
}
}
}
}
}
}
}
void SkyMap::drawDeepSkyCatalog( TQPainter& psky, TQPtrList<DeepSkyObject>& catalog, TQColor& color,
bool drawObject, bool drawImage, double scale )
{
if ( drawObject || drawImage ) { //don't do anything if nothing is to be drawn!
int Width = int( scale * width() );
int Height = int( scale * height() );
// Set color once
psky.setPen( color );
psky.setBrush( NoBrush );
TQColor colorHST = data->colorScheme()->colorNamed( "HSTColor" );
double maglim = Options::magLimitDrawDeepSky();
//FIXME
//disabling faint limits until the NGC/IC catalog has reasonable mags
//adjust maglimit for ZoomLevel
//double lgmin = log10(MINZOOM);
//double lgmax = log10(MAXZOOM);
//double lgz = log10(Options::zoomFactor());
//if ( lgz <= 0.75*lgmax ) maglim -= (Options::magLimitDrawDeepSky() - Options::magLimitDrawDeepSkyZoomOut() )*(0.75*lgmax - lgz)/(0.75*lgmax - lgmin);
//else
maglim = 40.0; //show all deep-sky objects
for ( DeepSkyObject *obj = catalog.first(); obj; obj = catalog.next() ) {
if ( checkVisibility( obj, fov(), XRange ) ) {
float mag = obj->mag();
//only draw objects if flags set and its brighter than maglim (unless mag is undefined (=99.9)
if ( mag > 90.0 || mag < (float)maglim ) {
TQPoint o = getXY( obj, Options::useAltAz(), Options::useRefraction(), scale );
if ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) {
double PositionAngle = findPA( obj, o.x(), o.y(), scale );
//Draw Image
if ( drawImage && Options::zoomFactor() > 5.*MINZOOM ) {
obj->drawImage( psky, o.x(), o.y(), PositionAngle, Options::zoomFactor(), scale );
}
//Draw Symbol
if ( drawObject ) {
//change color if extra images are available
// most objects don't have those, so we only change colors temporarily
// for the few exceptions. Changing color is expensive!!!
bool bColorChanged = false;
if ( obj->isCatalogM() && obj->ImageList.count() > 1 ) {
psky.setPen( colorHST );
bColorChanged = true;
} else if ( (!obj->isCatalogM()) && obj->ImageList.count() ) {
psky.setPen( colorHST );
bColorChanged = true;
}
obj->drawSymbol( psky, o.x(), o.y(), PositionAngle, Options::zoomFactor(), scale );
// revert temporary color change
if ( bColorChanged ) {
psky.setPen( color );
}
}
}
}
} else { //Object failed checkVisible(); delete it's Image pointer, if it exists.
if ( obj->image() ) {
obj->deleteImage();
}
}
}
}
}
void SkyMap::drawDeepSkyObjects( TQPainter& psky, double scale )
{
int Width = int( scale * width() );
int Height = int( scale * height() );
TQImage ScaledImage;
bool checkSlewing = ( ( slewing || ( clockSlewing && data->clock()->isActive() ) )
&& Options::hideOnSlew() );
//shortcuts to inform wheter to draw different objects
bool drawMess( Options::showDeepSky() && ( Options::showMessier() || Options::showMessierImages() ) && !(checkSlewing && Options::hideMessier() ) );
bool drawNGC( Options::showDeepSky() && Options::showNGC() && !(checkSlewing && Options::hideNGC() ) );
bool drawOther( Options::showDeepSky() && Options::showOther() && !(checkSlewing && Options::hideOther() ) );
bool drawIC( Options::showDeepSky() && Options::showIC() && !(checkSlewing && Options::hideIC() ) );
bool drawImages( Options::showMessierImages() );
// calculate color objects once, outside the loop
TQColor colorMess = data->colorScheme()->colorNamed( "MessColor" );
TQColor colorIC = data->colorScheme()->colorNamed( "ICColor" );
TQColor colorNGC = data->colorScheme()->colorNamed( "NGCColor" );
// draw Messier catalog
if ( drawMess ) {
drawDeepSkyCatalog( psky, data->deepSkyListMessier, colorMess, Options::showMessier(), drawImages, scale );
}
// draw NGC Catalog
if ( drawNGC ) {
drawDeepSkyCatalog( psky, data->deepSkyListNGC, colorNGC, true, drawImages, scale );
}
// draw IC catalog
if ( drawIC ) {
drawDeepSkyCatalog( psky, data->deepSkyListIC, colorIC, true, drawImages, scale );
}
// draw the rest
if ( drawOther ) {
//Use NGC color for now...
drawDeepSkyCatalog( psky, data->deepSkyListOther, colorNGC, true, drawImages, scale );
}
//Draw Custom Catalogs
for ( unsigned int i=0; i<data->CustomCatalogs.count(); ++i ) {
if ( Options::showCatalog()[i] ) {
TQPtrList<SkyObject> cat = data->CustomCatalogs.at(i)->objList();
for ( SkyObject *obj = cat.first(); obj; obj = cat.next() ) {
if ( checkVisibility( obj, fov(), XRange ) ) {
TQPoint o = getXY( obj, Options::useAltAz(), Options::useRefraction(), scale );
if ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) {
if ( obj->type()==0 ) {
StarObject *starobj = (StarObject*)obj;
float zoomlim = 7.0 + ( Options::zoomFactor()/MINZOOM)/50.0;
float mag = starobj->mag();
float sizeFactor = 2.0;
int size = int( sizeFactor*(zoomlim - mag) ) + 1;
if (size>23) size=23;
if ( size > 0 ) {
TQChar c = starobj->color();
TQPixmap *spixmap = starpix->getPixmap( &c, size );
// Try to paint with the selected color
spixmap->fill(TQColor( data->CustomCatalogs.at(i)->color() ));
starobj->draw( psky, sky, spixmap, o.x(), o.y(), true, scale );
// After drawing the star we display some extra info like
// the name ...
bool drawName = ( Options::showStarNames() && (starobj->name() != i18n("star") ) );
if ( !checkSlewing && (starobj->mag() <= Options::magLimitDrawStarInfo() )
&& ( drawName || Options::showStarMagnitudes() ) ) {
psky.setPen( TQColor( data->colorScheme()->colorNamed( "SNameColor" ) ) );
TQFont stdFont( psky.font() );
TQFont smallFont( stdFont );
smallFont.setPointSize( stdFont.pointSize() - 2 );
if ( Options::zoomFactor() < 10.*MINZOOM ) {
psky.setFont( smallFont );
} else {
psky.setFont( stdFont );
}
starobj->drawLabel( psky, o.x(), o.y(), Options::zoomFactor(), drawName, Options::showStarMagnitudes(), scale );
//reset font
psky.setFont( stdFont );
}
}
} else {
DeepSkyObject *dso = (DeepSkyObject*)obj;
double pa = findPA( dso, o.x(), o.y(), scale );
dso->drawImage( psky, o.x(), o.y(), pa, Options::zoomFactor() );
psky.setBrush( NoBrush );
psky.setPen( TQColor( data->CustomCatalogs.at(i)->color() ) );
dso->drawSymbol( psky, o.x(), o.y(), pa, Options::zoomFactor() );
}
}
}
}
}
}
}
void SkyMap::drawAttachedLabels( TQPainter &psky, double scale ) {
int Width = int( scale * width() );
int Height = int( scale * height() );
psky.setPen( data->colorScheme()->colorNamed( "UserLabelColor" ) );
bool checkSlewing = ( slewing || ( clockSlewing && data->clock()->isActive() ) ) && Options::hideOnSlew();
bool drawPlanets( Options::showPlanets() && !(checkSlewing && Options::hidePlanets() ) );
bool drawComets( drawPlanets && Options::showComets() );
bool drawAsteroids( drawPlanets && Options::showAsteroids() );
bool drawMessier( Options::showDeepSky() && ( Options::showMessier() || Options::showMessierImages() ) && !(checkSlewing && Options::hideMessier() ) );
bool drawNGC( Options::showDeepSky() && Options::showNGC() && !(checkSlewing && Options::hideNGC() ) );
bool drawIC( Options::showDeepSky() && Options::showIC() && !(checkSlewing && Options::hideIC() ) );
bool drawOther( Options::showDeepSky() && Options::showOther() && !(checkSlewing && Options::hideOther() ) );
bool drawSAO = ( Options::showStars() );
bool hideFaintStars( checkSlewing && Options::hideStars() );
for ( SkyObject *obj = data->ObjLabelList.first(); obj; obj = data->ObjLabelList.next() ) {
//Only draw an attached label if the object is being drawn to the map
//reproducing logic from other draw funcs here...not an optimal solution
if ( obj->type() == SkyObject::STAR ) {
if ( ! drawSAO ) return;
if ( obj->mag() > Options::magLimitDrawStar() ) return;
if ( hideFaintStars && obj->mag() > Options::magLimitHideStar() ) return;
}
if ( obj->type() == SkyObject::PLANET ) {
if ( ! drawPlanets ) return;
if ( obj->name() == "Sun" && ! Options::showSun() ) return;
if ( obj->name() == "Mercury" && ! Options::showMercury() ) return;
if ( obj->name() == "Venus" && ! Options::showVenus() ) return;
if ( obj->name() == "Moon" && ! Options::showMoon() ) return;
if ( obj->name() == "Mars" && ! Options::showMars() ) return;
if ( obj->name() == "Jupiter" && ! Options::showJupiter() ) return;
if ( obj->name() == "Saturn" && ! Options::showSaturn() ) return;
if ( obj->name() == "Uranus" && ! Options::showUranus() ) return;
if ( obj->name() == "Neptune" && ! Options::showNeptune() ) return;
if ( obj->name() == "Pluto" && ! Options::showPluto() ) return;
}
if ( obj->type() >= SkyObject::OPEN_CLUSTER && obj->type() <= SkyObject::GALAXY ) {
if ( ((DeepSkyObject*)obj)->isCatalogM() && ! drawMessier ) return;
if ( ((DeepSkyObject*)obj)->isCatalogNGC() && ! drawNGC ) return;
if ( ((DeepSkyObject*)obj)->isCatalogIC() && ! drawIC ) return;
if ( ((DeepSkyObject*)obj)->isCatalogNone() && ! drawOther ) return;
}
if ( obj->type() == SkyObject::COMET && ! drawComets ) return;
if ( obj->type() == SkyObject::ASTEROID && ! drawAsteroids ) return;
if ( checkVisibility( obj, fov(), XRange ) ) {
TQPoint o = getXY( obj, Options::useAltAz(), Options::useRefraction(), scale );
if ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) {
drawNameLabel( psky, obj, o.x(), o.y(), scale );
}
}
}
//Attach a label to the centered object
if ( focusObject() != NULL && Options::useAutoLabel() ) {
TQPoint o = getXY( focusObject(), Options::useAltAz(), Options::useRefraction(), scale );
if ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height )
drawNameLabel( psky, focusObject(), o.x(), o.y(), scale );
}
}
void SkyMap::drawNameLabel( TQPainter &psky, SkyObject *obj, int x, int y, double scale ) {
int size(0);
TQFont stdFont( psky.font() );
TQFont smallFont( stdFont );
smallFont.setPointSize( stdFont.pointSize() - 2 );
if ( Options::zoomFactor() < 10.*MINZOOM ) {
psky.setFont( smallFont );
} else {
psky.setFont( stdFont );
}
//Stars
if ( obj->type() == SkyObject::STAR ) {
((StarObject*)obj)->drawLabel( psky, x, y, Options::zoomFactor(), true, false, scale );
psky.setFont( stdFont );
return;
//Solar system
} else if ( obj->type() == SkyObject::PLANET
|| obj->type() == SkyObject::ASTEROID
|| obj->type() == SkyObject::COMET ) {
KSPlanetBase *p = (KSPlanetBase*)obj;
size = int( p->angSize() * scale * dms::PI * Options::zoomFactor()/10800.0 );
int minsize = 4;
if ( p->type() == SkyObject::ASTEROID || p->type() == SkyObject::COMET )
minsize = 2;
if ( p->name() == "Sun" || p->name() == "Moon" )
minsize = 8;
if ( size < minsize )
size = minsize;
if ( p->name() == "Saturn" )
size = int(2.5*size);
//Other
} else {
//Calculate object size in pixels
float majorAxis = ((DeepSkyObject*)obj)->a();
if ( majorAxis == 0.0 && obj->type() == 1 ) majorAxis = 1.0; //catalog stars
size = int( majorAxis * scale * dms::PI * Options::zoomFactor()/10800.0 );
}
int offset = int( ( 0.5*size + 4 ) );
psky.drawText( x+offset, y+offset, obj->translatedName() );
//Reset font
psky.setFont( stdFont );
}
void SkyMap::drawPlanetTrail( TQPainter& psky, KSPlanetBase *ksp, double scale )
{
if ( ksp->hasTrail() ) {
int Width = int( scale * width() );
int Height = int( scale * height() );
TQColor tcolor1 = TQColor( data->colorScheme()->colorNamed( "PlanetTrailColor" ) );
TQColor tcolor2 = TQColor( data->colorScheme()->colorNamed( "SkyColor" ) );
SkyPoint *p = ksp->trail()->first();
TQPoint o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
TQPoint cur( o );
bool doDrawLine(false);
int i = 0;
int n = ksp->trail()->count();
if ( ( o.x() >= -1000 && o.x() <= Width+1000 && o.y() >=-1000 && o.y() <= Height+1000 ) ) {
psky.moveTo(o.x(), o.y());
doDrawLine = true;
}
psky.setPen( TQPen( tcolor1, 1 ) );
for ( p = ksp->trail()->next(); p; p = ksp->trail()->next() ) {
if ( Options::fadePlanetTrails() ) {
//Define interpolated color
TQColor tcolor = TQColor(
(i*tcolor1.red() + (n-i)*tcolor2.red())/n,
(i*tcolor1.green() + (n-i)*tcolor2.green())/n,
(i*tcolor1.blue() + (n-i)*tcolor2.blue())/n );
++i;
psky.setPen( TQPen( tcolor, 1 ) );
}
o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
if ( ( o.x() >= -1000 && o.x() <= Width+1000 && o.y() >=-1000 && o.y() <= Height+1000 ) ) {
//Want to disable line-drawing if this point and the last are both outside bounds of display.
if ( ! TQT_TQRECT_OBJECT(rect()).contains( o ) && ! TQT_TQRECT_OBJECT(rect()).contains( cur ) ) doDrawLine = false;
cur = o;
if ( doDrawLine ) {
psky.lineTo( o.x(), o.y() );
} else {
psky.moveTo( o.x(), o.y() );
doDrawLine = true;
}
}
}
}
}
void SkyMap::drawSolarSystem( TQPainter& psky, bool drawPlanets, double scale )
{
int Width = int( scale * width() );
int Height = int( scale * height() );
if ( drawPlanets ) {
//Draw all trails first so they never appear "in front of" solar system bodies
//draw Trail
if ( Options::showSun() && data->PCat->findByName("Sun")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Sun"), scale );
if ( Options::showMercury() && data->PCat->findByName("Mercury")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Mercury"), scale );
if ( Options::showVenus() && data->PCat->findByName("Venus")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Venus"), scale );
if ( Options::showMoon() && data->Moon->hasTrail() ) drawPlanetTrail( psky, data->Moon, scale );
if ( Options::showMars() && data->PCat->findByName("Mars")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Mars"), scale );
if ( Options::showJupiter() && data->PCat->findByName("Jupiter")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Jupiter"), scale );
if ( Options::showSaturn() && data->PCat->findByName("Saturn")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Saturn"), scale );
if ( Options::showUranus() && data->PCat->findByName("Uranus")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Uranus"), scale );
if ( Options::showNeptune() && data->PCat->findByName("Neptune")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Neptune"), scale );
if ( Options::showPluto() && data->PCat->findByName("Pluto")->hasTrail() ) drawPlanetTrail( psky, data->PCat->findByName("Pluto"), scale );
if ( Options::showAsteroids() ) {
for ( KSAsteroid *ast = data->asteroidList.first(); ast; ast = data->asteroidList.next() ) {
if ( ast->mag() > Options::magLimitAsteroid() ) break;
if ( ast->hasTrail() ) drawPlanetTrail( psky, ast, scale );
}
}
if ( Options::showComets() ) {
for ( KSComet *com = data->cometList.first(); com; com = data->cometList.next() ) {
if ( com->hasTrail() ) drawPlanetTrail( psky, com, scale );
}
}
//Now draw the actual solar system bodies. Draw furthest to closest.
//Draw Asteroids
if ( Options::showAsteroids() ) {
for ( KSAsteroid *ast = data->asteroidList.first(); ast; ast = data->asteroidList.next() ) {
if ( ast->mag() > Options::magLimitAsteroid() ) break;
if ( checkVisibility( ast, fov(), XRange ) ) {
psky.setPen( TQPen( TQColor( "gray" ) ) );
psky.setBrush( TQBrush( TQColor( "gray" ) ) );
TQPoint o = getXY( ast, Options::useAltAz(), Options::useRefraction(), scale );
if ( ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) ) {
int size = int( ast->angSize() * scale * dms::PI * Options::zoomFactor()/10800.0 );
if ( size < 1 ) size = 1;
int x1 = o.x() - size/2;
int y1 = o.y() - size/2;
psky.drawEllipse( x1, y1, size, size );
//draw Name
if ( Options::showAsteroidNames() && ast->mag() < Options::magLimitAsteroidName() ) {
psky.setPen( TQColor( data->colorScheme()->colorNamed( "PNameColor" ) ) );
drawNameLabel( psky, ast, o.x(), o.y(), scale );
}
}
}
}
}
//Draw Comets
if ( Options::showComets() ) {
for ( KSComet *com = data->cometList.first(); com; com = data->cometList.next() ) {
if ( checkVisibility( com, fov(), XRange ) ) {
psky.setPen( TQPen( TQColor( "cyan4" ) ) );
psky.setBrush( TQBrush( TQColor( "cyan4" ) ) );
TQPoint o = getXY( com, Options::useAltAz(), Options::useRefraction(), scale );
if ( ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) ) {
int size = int( com->angSize() * scale * dms::PI * Options::zoomFactor()/10800.0 );
if ( size < 1 ) size = 1;
int x1 = o.x() - size/2;
int y1 = o.y() - size/2;
psky.drawEllipse( x1, y1, size, size );
//draw Name
if ( Options::showCometNames() && com->rsun() < Options::maxRadCometName() ) {
psky.setPen( TQColor( data->colorScheme()->colorNamed( "PNameColor" ) ) );
drawNameLabel( psky, com, o.x(), o.y(), scale );
}
}
}
}
}
//Draw Pluto
if ( Options::showPluto() ) {
drawPlanet(psky, data->PCat->findByName("Pluto"), TQColor( "gray" ), 50.*MINZOOM, 1, scale );
}
//Draw Neptune
if ( Options::showNeptune() ) {
drawPlanet(psky, data->PCat->findByName("Neptune"), TQColor( "SkyBlue" ), 20.*MINZOOM, 1, scale );
}
//Draw Uranus
if ( Options::showUranus() ) {
drawPlanet(psky, data->PCat->findByName("Uranus"), TQColor( "LightSeaGreen" ), 20.*MINZOOM, 1, scale );
}
//Draw Saturn
if ( Options::showSaturn() ) {
drawPlanet(psky, data->PCat->findByName("Saturn"), TQColor( "LightYellow2" ), 20.*MINZOOM, 2, scale );
}
//Draw Jupiter and its moons.
//Draw all moons first, then Jupiter, then redraw moons that are in front of Jupiter.
if ( Options::showJupiter() ) {
//Draw Jovian moons
psky.setPen( TQPen( TQColor( "white" ) ) );
if ( Options::zoomFactor() > 10.*MINZOOM ) {
for ( unsigned int i=0; i<4; ++i ) {
TQPoint o = getXY( data->jmoons->pos(i), Options::useAltAz(), Options::useRefraction(), scale );
if ( ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) ) {
psky.drawEllipse( o.x()-1, o.y()-1, 2, 2 );
}
}
}
drawPlanet(psky, data->PCat->findByName("Jupiter"), TQColor( "Goldenrod" ), 20.*MINZOOM, 1, scale );
//Re-draw Jovian moons which are in front of Jupiter, also draw all 4 moon labels.
psky.setPen( TQPen( TQColor( "white" ) ) );
if ( Options::zoomFactor() > 10.*MINZOOM ) {
TQFont pfont = psky.font();
TQFont moonFont = psky.font();
moonFont.setPointSize( pfont.pointSize() - 2 );
psky.setFont( moonFont );
for ( unsigned int i=0; i<4; ++i ) {
TQPoint o = getXY( data->jmoons->pos(i), Options::useAltAz(), Options::useRefraction(), scale );
if ( ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) ) {
if ( data->jmoons->z(i) < 0.0 ) //Moon is nearer than Jupiter
psky.drawEllipse( o.x()-1, o.y()-1, 2, 2 );
//Draw Moon name labels if at high zoom
if ( Options::showPlanetNames() && Options::zoomFactor() > 50.*MINZOOM ) {
int offset = int(3*scale);
psky.drawText( o.x() + offset, o.y() + offset, data->jmoons->name(i) );
}
}
}
//reset font
psky.setFont( pfont );
}
}
//Draw Mars
if ( Options::showMars() ) {
drawPlanet(psky, data->PCat->findByName("Mars"), TQColor( "Red" ), 20.*MINZOOM, 1, scale );
}
//For the inner planets, we need to determine the distance-order
//because the order can change with time
double rv = data->PCat->findByName("Venus")->rearth();
double rm = data->PCat->findByName("Mercury")->rearth();
double rs = data->PCat->findByName("Sun")->rearth();
unsigned int iv(0), im(0), is(0);
if ( rm > rs ) im++;
if ( rm > rv ) im++;
if ( rv > rs ) iv++;
if ( rv > rm ) iv++;
if ( rs > rm ) is++;
if ( rs > rv ) is++;
for ( unsigned int i=0; i<3; i++ ) {
if ( i==is ) {
//Draw Sun
if ( Options::showSun() ) {
drawPlanet(psky, data->PCat->findByName("Sun"), TQColor( "Yellow" ), MINZOOM, 1, scale );
}
} else if ( i==im ) {
//Draw Mercury
if ( Options::showMercury() ) {
drawPlanet(psky, data->PCat->findByName("Mercury"), TQColor( "SlateBlue1" ), 20.*MINZOOM, 1, scale );
}
} else if ( i==iv ) {
//Draw Venus
if ( Options::showVenus() ) {
drawPlanet(psky, data->PCat->findByName("Venus"), TQColor( "LightGreen" ), 20.*MINZOOM, 1, scale );
}
}
}
//Draw Moon
if ( Options::showMoon() ) {
drawPlanet(psky, data->Moon, TQColor( "White" ), MINZOOM, 1, scale );
}
}
}
void SkyMap::drawPlanet( TQPainter &psky, KSPlanetBase *p, TQColor c,
double zoommin, int resize_mult, double scale ) {
if ( checkVisibility( p, fov(), XRange ) ) {
int Width = int( scale * width() );
int Height = int( scale * height() );
int sizemin = 4;
if ( p->name() == "Sun" || p->name() == "Moon" ) sizemin = 8;
sizemin = int( sizemin * scale );
psky.setPen( c );
psky.setBrush( c );
TQPoint o = getXY( p, Options::useAltAz(), Options::useRefraction(), scale );
//Is planet onscreen?
if ( o.x() >= 0 && o.x() <= Width && o.y() >= 0 && o.y() <= Height ) {
int size = int( p->angSize() * scale * dms::PI * Options::zoomFactor()/10800.0 );
if ( size < sizemin ) size = sizemin;
//Draw planet image if:
if ( Options::showPlanetImages() && //user wants them,
int(Options::zoomFactor()) >= int(zoommin) && //zoomed in enough,
!p->image()->isNull() && //image loaded ok,
size < Width ) { //and size isn't too big.
//Image size must be modified to account for possibility that rotated image's
//size is bigger than original image size. The rotated image is a square
//superscribed on the original image. The superscribed square is larger than
//the original square by a factor of (cos(t) + sin(t)) where t is the angle by
//which the two squares are rotated (in our case, equal to the position angle +
//the north angle, reduced between 0 and 90 degrees).
//The proof is left as an exercise to the student :)
dms pa( findPA( p, o.x(), o.y(), scale ) );
double spa, cpa;
pa.SinCos( spa, cpa );
cpa = fabs(cpa);
spa = fabs(spa);
size = int( size * (cpa + spa) );
//Because Saturn has rings, we inflate its image size by a factor 2.5
if ( p->name() == "Saturn" ) size = int(2.5*size);
if (resize_mult != 1) {
size *= resize_mult;
}
p->scaleRotateImage( size, pa.Degrees() );
int x1 = o.x() - p->image()->width()/2;
int y1 = o.y() - p->image()->height()/2;
psky.drawImage( x1, y1, *(p->image()));
} else { //Otherwise, draw a simple circle.
psky.drawEllipse( o.x()-size/2, o.y()-size/2, size, size );
}
//draw Name
if ( Options::showPlanetNames() ) {
psky.setPen( TQColor( data->colorScheme()->colorNamed( "PNameColor" ) ) );
drawNameLabel( psky, p, o.x(), o.y(), scale );
}
}
}
}
void SkyMap::exportSkyImage( const TQPaintDevice *pd ) {
TQPainter p;
//shortcuts to inform wheter to draw different objects
bool drawPlanets( Options::showPlanets() );
bool drawMW( Options::showMilkyWay() );
bool drawCNames( Options::showCNames() );
bool drawCLines( Options::showCLines() );
bool drawCBounds( Options::showCBounds() );
bool drawGrid( Options::showGrid() );
p.begin( const_cast<TQPaintDevice*>(pd) );
TQPaintDeviceMetrics pdm( p.device() );
//scale image such that it fills 90% of the x or y dimension on the paint device
double xscale = double(pdm.width()) / double(width());
double yscale = double(pdm.height()) / double(height());
double scale = (xscale < yscale) ? xscale : yscale;
int pdWidth = int( scale * width() );
int pdHeight = int( scale * height() );
int x1 = int( 0.5*(pdm.width() - pdWidth) );
int y1 = int( 0.5*(pdm.height() - pdHeight) );
p.setClipRect( TQRect( x1, y1, pdWidth, pdHeight ) );
p.setClipping( true );
//Fill background with sky color
p.fillRect( x1, y1, pdWidth, pdHeight, TQBrush( TQColor(data->colorScheme()->colorNamed( "SkyColor" ) )) );
if ( x1 || y1 ) p.translate( x1, y1 );
if ( drawMW ) drawMilkyWay( p, scale );
if ( drawGrid ) drawCoordinateGrid( p, scale );
if ( drawCBounds ) drawConstellationBoundaries( p, scale );
if ( drawCLines ) drawConstellationLines( p, scale );
if ( drawCNames ) drawConstellationNames( p, scale );
if ( Options::showEquator() ) drawEquator( p, scale );
if ( Options::showEcliptic() ) drawEcliptic( p, scale );
drawStars( p, scale );
drawDeepSkyObjects( p, scale );
drawSolarSystem( p, drawPlanets, scale );
drawAttachedLabels( p, scale );
drawObservingList( p, scale );
drawHorizon( p, scale );
p.end();
}
void SkyMap::setMapGeometry() {
guidemax = int(Options::zoomFactor()/10.0);
isPoleVisible = false;
if ( Options::useAltAz() ) {
XRange = 1.2*fov()/cos( focus()->alt()->radians() );
Ymax = fabs( focus()->alt()->Degrees() ) + fov();
} else {
XRange = 1.2*fov()/cos( focus()->dec()->radians() );
Ymax = fabs( focus()->dec()->Degrees() ) + fov();
}
if ( Ymax >= 90. ) isPoleVisible = true;
//at high zoom, double FOV for guide lines so they don't disappear.
guideFOV = fov();
guideXRange = XRange;
if ( Options::zoomFactor() > 10.*MINZOOM ) { guideFOV *= 2.0; guideXRange *= 2.0; }
}