// 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 program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 

// Copyright Liam Girdwood 2003

#include "sky_proj.hh"

/*! \namespace Vega
* \brief Nova Virtual Sky Engine
*/
namespace Vega
{

/*! \fn SkyProj::SkyProj()
* Constructor
*/
SkyProj::SkyProj()
{
	// default sky settings
	m_centre_ra = MAX_RA / 2;
	m_centre_dec = 0;
	m_fov = MAX_RA / 2;
	m_clip_mag_max = 4;
	m_clip_mag_min = -2;	
	m_show_const_lines = true;		
	m_show_const_bounds = true;	
	m_show_const_names = true;
	m_show_grid = true;
	m_show_planets = true;
	m_root = root();
	
	// canvas groups
	m_object_group = 0;
	m_grid_group = 0;
	m_const_group = 0;
}

void SkyProj::clone (SkyProj* proj)
{
	m_centre_ra = proj->m_centre_ra;
	m_centre_dec = proj->m_centre_dec;
	m_fov = proj->m_fov;
	m_show_const_lines = proj->m_show_const_lines;
	m_show_const_bounds = proj->m_show_const_bounds;
	m_show_const_names = proj->m_show_const_names;
	m_show_grid = proj->m_show_grid;
	m_JD = proj->m_JD;
}

/*! \fn void SkyProj::show_sky()
* 
* Create and show the sky projection.
*/
void SkyProj::show_sky()
{
	set_pixels_per_unit(1);
	set_scroll_region(0, 0, m_canvas_width, m_canvas_height);
	
	// render sky
	render_background();
	render_sky(FULL);
}

/*! \fn SkyProj::~SkyProj()
* Destructor
*/
SkyProj::~SkyProj()
{
}

/*! \fn void SkyProj::show_grid(bool show)
*
* Set whether a position marker is rendered
*/
void SkyProj::show_grid(bool show)
{
	m_show_grid = show;
}

/*! \fn void SkyProj::show_const_lines(bool show)
*
* Set whether constellation outlines are rendered
*/
void SkyProj::show_const_lines(bool show)
{
	m_show_const_lines = show;
}

/*! \fn void SkyProj::show_const_names(bool show)
*
* Set whether constellation names are rendered
*/
void SkyProj::show_const_names(bool show)
{
	m_show_const_names = show;
}

/*! \fn void SkyProj::show_const_bounds(bool show)
*
* Set whether constellation boundaries are rendered
*/
void SkyProj::show_const_bounds(bool show)
{
	m_show_const_bounds = show;
}

/*! \fn bool SkyProj::show_grid()
*
* True if position marker is rendered
*/
bool SkyProj::is_grid()
{
	return m_show_grid;
}

/*! \fn bool SkyProj::is_const_lines()
*
* True if constellation outlines are rendered
*/
bool SkyProj::is_const_lines()
{
	return m_show_const_lines;
}

/*! \fn bool SkyProj::is_const_names()
*
* True if constellation names are rendered
*/
bool SkyProj::is_const_names()
{
	return m_show_const_names;
}

/*! \fn bool SkyProj::is_const_bounds()
*
* True if constellation boundaries are rendered
*/
bool SkyProj::is_const_bounds()
{
	return m_show_const_bounds;
}

/*! \fn void SkyProj::set_jd (double jd)
*
* Set sky projection Julian day
*/
void SkyProj::set_jd (double jd)
{
	m_JD = jd;
}

/*! \fn double SkyProj::get_jd()
*
* Get sky projection Julian day
*/
double SkyProj::get_jd()
{
	return m_JD;
}

/*! \fn void SkyProj::add_bright_catalog(Pollux::Catalog* cat)
* \param cat Bright object catalog
* 
* Add a bright object catalog to the sky projection
*/
void SkyProj::add_bright_catalog(Pollux::Catalog* cat)
{
	m_bright_cat = cat;
}

/*! \fn void SkyProj::set_size(int width, int height)
*
* Set the sky projection size
*/
void SkyProj::set_size(int width, int height)
{
	m_canvas_height = height;
	m_canvas_width = width;
	std::cout << " height " << m_canvas_height << " width " << m_canvas_width << std::endl;
	calc_fov_bounds();
}

/*! \fn void SkyProj::calc_fov_bounds()
*
* Calculate the boundaries, magnitude level and field of view for the
* virtual sky projection.
*/
void SkyProj::calc_fov_bounds()
{
	m_canvas_ratio = (double)m_canvas_width / (double)m_canvas_height;
	m_is_ra_over = false;
	m_is_ra_under = false;
	m_ra_offset = 0;
	m_dec_offset = 0;
	
	if (m_spherical)
		m_clip_ra_min = m_centre_ra - (m_fov / 2.0) * m_canvas_ratio;
	else
		m_clip_ra_min = m_centre_ra - (m_fov / 2.0);
	
	if (m_clip_ra_min < MIN_RA) {
		m_is_ra_under = true;
		m_ra_offset = m_centre_ra - (m_fov / 2.0);
		m_clip_ra_min = MIN_RA;
	}
	
	if (m_spherical)
		m_clip_ra_max = m_centre_ra + (m_fov / 2.0) * m_canvas_ratio;
	else
		m_clip_ra_max = m_centre_ra + (m_fov / 2.0);
	
	if (m_clip_ra_max > MAX_RA) {
		m_is_ra_over = true;
		m_ra_offset = m_centre_ra + (m_fov / 2.0);
		m_clip_ra_max = MAX_RA;
	}
	
	if (m_spherical)
		m_clip_dec_min = m_centre_dec - (m_fov / 2.0);
	else
		m_clip_dec_min = m_centre_dec - (m_fov / 2.0) / m_canvas_ratio;
	
	if (m_clip_dec_min <= MIN_DEC) {
		m_dec_offset = m_clip_dec_min - MIN_DEC;
		m_clip_dec_min = MIN_DEC;
	}
	
	if (m_spherical)
		m_clip_dec_max = m_centre_dec + (m_fov / 2.0);
	else
		m_clip_dec_max = m_centre_dec + (m_fov / 2.0) / m_canvas_ratio;
	
	if (m_clip_dec_max > MAX_DEC) {
		m_clip_dec_max = MAX_DEC;
	}
	
	// are we over/under lapping
	if (m_is_ra_over || m_is_ra_under)
			calc_ra_overlap();
	
	m_ra_ppd = m_canvas_width / m_fov;
	m_dec_ppd = m_canvas_height / (m_fov / m_canvas_ratio);
	
	// a look up table - there must be a better way......
	double mag_const = MAG_CONST;
	if (m_fov < 145.0 && m_fov >= 60.0)
		mag_const *= 1.65;
	else if (m_fov < 60.0 && m_fov >= 35.0)
		mag_const *= 2.0;
	else if (m_fov < 35.0 && m_fov >= 10.0)
		mag_const *= 2.5;
	else if (m_fov < 10.0 && m_fov >= 5.0)
		mag_const *= 3.0;
	else if (m_fov < 5.0 && m_fov >= 2.0)
		mag_const *= 4.0;
	else if (m_fov < 2.0)
		mag_const *= 5.0;
	
	m_clip_mag_max = MAG_BASE + (MAX_FOV - m_fov) * mag_const;
	std::cout << " mag max " << m_clip_mag_max << " fov " << m_fov << std::endl;
	std::cout << " min ra " << m_clip_ra_min <<" max " << m_clip_ra_max << std::endl;
	std::cout << " min dec " << m_clip_dec_min << " max " << m_clip_dec_max << std::endl;
}

/*! \fn void SkyProj::move_ra (double pixels)
* \param pixels Number of pixels to move the sky projection in RA.
*
* Move the virtual sky east or west in RA.
*/
void SkyProj::move_ra (double pixels)
{	
	m_centre_ra += pixels / m_ra_ppd;
	
	// do we need to reset RA if there is a large overlap
	if (m_centre_ra > MAX_RA) {
		m_centre_ra = MIN_RA;	
	} else if (m_centre_ra < MIN_RA) {
		m_centre_ra = MAX_RA; 
	}
}

/*! \fn void SkyProj::move_dec (double pixels)
* \param pixels Number of pixels to move the sky projection in DEC.
*
* Move the virtual sky north or south in DEC.
*/
void SkyProj::move_dec (double pixels)
{
	// are we at the limits
	if ((m_centre_dec == MAX_DEC && pixels > 0) ||
		(m_centre_dec == MIN_DEC && pixels < 0))
		return;	
	
	m_centre_dec += pixels / m_dec_ppd;
	
	// do we need to reset DEC
	if (m_centre_dec > MAX_DEC) {
		m_centre_dec = MAX_DEC; 	
	} else if (m_centre_dec < MIN_DEC) {
		m_centre_dec = MIN_DEC; 
	}
}

/*! \fn bool SkyProj::zoom (double zoom)
* \param zoom Zoom in/out factor
* \return false if the sky cannot be zoomed.
*
* Zoom the sky projection in or out.
*/
bool SkyProj::zoom (double zoom)
{
	// are we at the limits
	if (m_fov * zoom >= MAX_FOV) {
		m_fov = MAX_FOV;
		return false;
	}
	if (m_fov * zoom <= MIN_FOV) {
		m_fov = MIN_FOV;
		return false;	
	}
	
	m_fov *= zoom;
	return true;
}

/*! \fn double SkyProj::calc_size (Castor::AstroObject* object)
* \param object Astro object
* \return Virtual sky canvas size in pixels.
*
* Calculate the sky projection canvas size of an object based 
* on it's visual magnitude.
*/
double SkyProj::calc_size (Castor::AstroObject* object)
{
	double mag = object->get_mag();
	return (m_clip_mag_max + 1) - mag;
}

/*! \fn void SkyProj::render_bg()
* 
* Render the sky projection black background
*/
void SkyProj::render_background()
{
	m_bg_group = Gtk::manage (new Gnome::Canvas::Group (*m_root, 0, 0));
	m_bg_rect = Gtk::manage (new Gnome::Canvas::Rect(*m_bg_group, 0, 0, m_canvas_width, m_canvas_height));
	*m_bg_rect << Gnome::Canvas::Properties::fill_color("black");
	m_bg_rect->set_data (Glib::Quark ("background"), (gpointer)0);
}

/*! \fn calc_ra_overlap()
*
* Calculate the projection RA over/under lap size and clipping area.
*/
void SkyProj::calc_ra_overlap()
{
	if (m_is_ra_under) {
		// we are < MIN_RA, so extra is rhs (under) OK
		m_clip_ra_min_overlap = MAX_RA + m_ra_offset;
		m_clip_ra_max_overlap = MAX_RA;
		m_clip_dec_min_overlap = m_clip_dec_min;
		m_clip_dec_max_overlap = m_clip_dec_max;
	} else {
		// we are > MIN_RA, so extra is lhs (over) OK
		m_clip_ra_min_overlap = MIN_RA;
		m_clip_ra_max_overlap = m_ra_offset - MAX_RA;
		m_clip_dec_min_overlap = m_clip_dec_min;
		m_clip_dec_max_overlap = m_clip_dec_max;
	}
}

/*! \fn void SkyProj::render_sky(Render type)
* 
* Update the Sky projection canvas.
* The update can either be for bright objects (fast) or for
* faint objects and other artifacts (slower).
* The fast update is used for moving and zooming around the sky.
*/
void SkyProj::render_sky(Render type)
{
	Glib::TimeVal t1, t2;
	t1.assign_current_time();
	std::cout << "Update started " << std::endl;
	
	calc_fov_bounds();
	
	// delete existing objects if we are starting a new update
	if (type != FAINT) {
		if (m_object_group) {
			delete m_object_group;
			m_object_group = 0;
		}
		if (m_const_group) {
			delete m_const_group;
			m_const_group = 0;
		}
		if (m_grid_group) {
			delete m_grid_group;
			m_grid_group = 0;
		}
	}
	
	if (type == BRIGHT || type == FULL) {
		if (m_show_grid) {
			m_grid_group = Gtk::manage (new Gnome::Canvas::Group (*m_root, 0, 0));
			render_grid();
		}
		
		// render all bright objects
		m_object_group = Gtk::manage (new Gnome::Canvas::Group (*m_root, 0, 0));
		render_planets();
		render_bright_objects(type);
		
	}
	if (type == FAINT || type == FULL) {
		
		// do we need to render constellations boundaries/names
		if (m_show_const_bounds || m_show_const_names || m_show_const_lines) {
			m_const_group = Gtk::manage (new Gnome::Canvas::Group (*m_root, 0, 0));
			m_const_group->lower_to_bottom();
			m_const_group->raise(1);
			
			if (m_show_const_lines)
				render_const_lines();
			if (m_show_const_bounds)
				render_const_bounds();
			if (m_show_const_names)
				render_const_names();
		}
		
		// render faint objects
		render_remaining_objects();
	}
	
	t2.assign_current_time();
	t2 = t2 - t1;
	std::cout << "Update Complete " << t2.as_double() << std::endl;
}

/*! \fn void SkyFlat::render_bright_objects(Render type)
*
* Render the bright objects on the 
* entire sky projection.
*/
void SkyProj::render_bright_objects(Render type)
{	
	render_basic();
	render_main_sector(type);
	if (m_is_ra_over || m_is_ra_under)
			render_overlap_sector(type);
	update_now();
}

/*! \fn void SkyFlat::render_bright_objects()
*
* Render the faint and other objects on the 
* entire sky projection.
*/
void SkyProj::render_remaining_objects()
{
	render_main_sector(FAINT);
	if (m_is_ra_over || m_is_ra_under)
			render_overlap_sector(FAINT);
}

/*! \fn void SkyFlat::render_main_sector()
*
* Render <type> objects on the sky projection
* main sector.
*/
void SkyProj::render_main_sector(Render type)
{
	std::vector<Castor::AstroObject*>::iterator i;
	std::vector<Castor::AstroObject*> object_band[OBJECT_GROUPS];
	std::vector<Castor::AstroObject*> objects;
	
	double x, y, size, ra_offset;
	double mag_min, mag_max;
	int index;
	bool bright;
	
	// are we only rendering bright stars
	if (type == BRIGHT) {
		mag_max = m_clip_mag_max - BRIGHT_DIFF;
		mag_min = m_clip_mag_min;
		bright = true;
	} else {
		if (type == FULL) {
			mag_max = m_clip_mag_max - BRIGHT_DIFF;
			mag_min = m_clip_mag_min;
			bright = false;
		} else {
			mag_max = m_clip_mag_max;
			mag_min = m_clip_mag_max - BRIGHT_DIFF;
			bright = false;
		}
	}
	
	// clip the catalog
	m_bright_cat->clip(m_clip_ra_min, m_clip_dec_min, 
				m_clip_ra_max , m_clip_dec_max,
				mag_min, mag_max);
	
	// get visible objects
	std::cout << " objects " << m_bright_cat->get_objects(objects) << std::endl;
	
	// sort into magnitude bands
	for (i = objects.begin(); i != objects.end(); i++) {
		index = (int)((*i)->get_mag());
		object_band[index + 2].push_back(*i);
	}
	
	// calc offset
	if (m_is_ra_over)
		ra_offset = MIN_RA;
	else
		ra_offset = m_ra_offset;
	
	// render objects, brightest first
	for (int j=0; j<OBJECT_GROUPS; j++) {
		for (i = object_band[j].begin(); i != object_band[j].end(); i++) {
			(*i)->get_equ_posn(0, x, y);
			transform(x, y, ra_offset);
			if (is_visible(x,y))
				(*i)->render(x, y, m_clip_mag_max, *m_object_group, bright);
		}
	}
}

/*! \fn void SkyFlat::render_main_sector()
*
* Render <type> objects on the sky projection
* overlap sector.
*/
void SkyProj::render_overlap_sector(Render type)
{
	std::vector<Castor::AstroObject*>::iterator i;
	std::vector<Castor::AstroObject*> object_band[OBJECT_GROUPS];
	std::vector<Castor::AstroObject*> objects;
	
	double x, y, size, ra_offset;
	double mag_min, mag_max;
	int index;
	bool bright;
	
	// are we only rendering bright stars
	if (type == BRIGHT) {
		mag_max = m_clip_mag_max - BRIGHT_DIFF;
		mag_min = m_clip_mag_min;
		bright = true;
	} else {
		mag_max = m_clip_mag_max;
		mag_min = m_clip_mag_max - BRIGHT_DIFF;
		bright = false;
	}

	// clip the catalog
	m_bright_cat->clip(m_clip_ra_min_overlap, m_clip_dec_min_overlap, 
				m_clip_ra_max_overlap , m_clip_dec_max_overlap,
				mag_min, mag_max);
	
	// get visible objects
	std::cout << " overlap objects " << m_bright_cat->get_objects(objects) << std::endl;
	
	// sort into magnitude band
	for (i = objects.begin(); i != objects.end(); i++) {
		index = (int)((*i)->get_mag());
		object_band[index + 2].push_back(*i);
	}
	
	// get ra offset
	if (m_is_ra_under)
		ra_offset = m_ra_offset + MAX_RA; //rhs
	else
		ra_offset = -MAX_RA; //lhs
	
	// render objects, brightest first
	for (int j=0; j<OBJECT_GROUPS; j++) {
		for (i = object_band[j].begin(); i != object_band[j].end(); i++) {
			(*i)->get_equ_posn(0, x, y);
			transform(x, y, ra_offset);
			if (is_visible(x,y))
				(*i)->render(x, y, m_clip_mag_max, *m_object_group, bright);
		}
	}
}

/*! \fn double SkyProj::get_ra ()
* \return RA (degrees)
*
* Get the current sky projection centre RA.
*/
double SkyProj::get_ra ()
{
	return m_centre_ra;
}

/*! \fn double SkyProj::get_dec ()
* \return DEC (degrees)
*
* Get the current sky projection centre DEC.
*/
double SkyProj::get_dec ()
{
	return m_centre_dec;
}

/*! \fn double SkyProj::get_fov ();
* \return FOV (degrees)
* 
* Get the sky projection field of view in degrees
*/
double SkyProj::get_fov ()
{
	return m_fov;
}

/*! \fn void SkyProj::set_ra (double ra)
*
* Set the sky projection centre RA.
*/
void SkyProj::set_ra (double ra)
{
	m_centre_ra = ra;
}

/*! \fn void SkyProj::set_dec (double dec)
*
* Set the sky projection centre DEC.
*/
void SkyProj::set_dec (double dec)
{
	m_centre_dec = dec;
}

/*! \fn void SkyProj::set_fov (double fov)
*
* Set the sky projection field of view in degrees.
*/
void SkyProj::set_fov (double fov)
{
	m_fov = fov;
}

}


syntax highlighted by Code2HTML, v. 0.9.1