/***********************************************************************
* map.c : Implementation of the map.
***********************************************************************/
/***********************************************************************
* This file is part of SpaceChart.
* Copyright (C) 2000 Miguel Coca <e970095@zipi.fi.upm.es>
*
* 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.
***********************************************************************/
#include "../include/map_widget.h"
#include "../include/map.h"
#include "../include/starmap.h"
#include "../include/star.h"
#include "../include/link.h"
#include "../include/star_index.h"
#include "../include/link_index.h"
#include "../include/perspective.h"
#include "../include/settings.h"
struct st_map
{
settings_t *settings;
map_widget_t *map_widget;
link_t **link_list;
link_index_t *link_index;
star_t **star_list;
star_index_t *star_index;
perspective_t *persp;
void (*star_clicked)( star_t* star, void* data );
void* star_clicked_data;
GdkFont *font;
/* Data needed for checking which star was clicked on */
double lambda;
double clicked_x, clicked_y;
/* This is a kludge to keep know when a callback from the sight params
* comes from our own changes. */
int changed_sight;
};
/* Definition of local functions */
static void cb_clicked_on_map( map_widget_t* map_widget, double x, double y,
double lambda, map_t *map );
static void cb_redraw_map( map_widget_t* map_widget, map_t *map );
static void cb_rotate_map( map_widget_t* map_widget, double v_ang,
double h_ang, map_t *map );
static void cb_view_radius(settings_t* settings, map_t *map);
static void cb_drawing_settings(settings_t* settings, map_t *map);
static void cb_sight_params(settings_t* settings, map_t *map);
static void cb_center(settings_t* settings, map_t *map);
static int cb_is_clicked_star(star_t* star, double x, double y, map_t *map );
static void build_indexes( map_t *map );
static void draw( map_t *map );
static void draw_star( star_t* star, double x, double y, map_t* map );
static void draw_link( link_t* link, double x1, double y1,
double x2, double y2, map_t* map );
/* Public functions */
map_t* map_new( settings_t* settings )
{
map_t* map;
if( (map = (map_t*) malloc( sizeof( struct st_map ) ) ) )
{
map->settings = settings;
map->map_widget = map_widget_new(
(void (*)(map_widget_t*, void*)) cb_redraw_map,
(void (*)( map_widget_t*, double v_ang,
double h_ang, void* data )) cb_rotate_map,
(void (*)( map_widget_t*, double, double, double,
void* )) cb_clicked_on_map,
map );
map->link_list = NULL;
map->link_index = NULL;
map->star_list = NULL;
map->star_index = NULL;
map->star_clicked = NULL;
map->star_clicked_data = NULL;
map->changed_sight = 0;
map->font = NULL;
/* Set up the perspective. */
map->persp = NULL;
/* Now we set the callbacks we want from the settings. */
settings_add_callback( settings, PROPERTIES_VIEW_RADIUS,
(void(*)(settings_t*, void*))
cb_view_radius, map );
settings_add_callback( settings,
PROPERTIES_SHOW_LINKS |
PROPERTIES_SHOW_LINK_LABELS |
PROPERTIES_SHOW_STAR_LABELS |
PROPERTIES_LABELS_COLOR |
PROPERTIES_LABELS_FONT |
PROPERTIES_DISTANCE_UNIT |
PROPERTIES_STAR_DRAW_RULES |
PROPERTIES_LINK_DRAW_RULES,
(void(*)(settings_t*, void*))
cb_drawing_settings, map );
settings_add_callback( settings,
PROPERTIES_SIGHT_PARAMS,
(void(*)(settings_t*, void*))
cb_sight_params, map );
settings_add_callback( settings,
PROPERTIES_CENTER,
(void(*)(settings_t*, void*))
cb_center, map );
}
return map;
}
/* Returns the widget that contains the map. */
GtkWidget* map_get_widget( map_t* map )
{
return map_widget_get_widget( map->map_widget );
}
/* Get the settings data structure used by the map. */
settings_t* map_get_settings( map_t* map )
{
return map->settings;
}
/* Set the function called when the user clicks on a star. */
void map_set_star_clicked( map_t* map,
void (*clicked)( star_t* star, void* data ),
void* data )
{
map->star_clicked = clicked;
map->star_clicked_data = data;
}
void map_rotate( map_t* map, double v_ang, double h_ang )
{
coords_3d_t from, up, center;
double distance;
/* Rotate and redraw */
perspective_rotate( map->persp, v_ang, h_ang );
build_indexes( map );
draw( map );
/* Now we tell the settings where we are. We remember to tell ourselves
to ignore the callback from the settings. */
map->changed_sight = 1;
perspective_get_up( map->persp, &up );
perspective_get_from( map->persp, &from );
perspective_get_center( map->persp, ¢er );
from.x -= center.x;
from.y -= center.y;
from.z -= center.z;
distance = magnitude( &from );
from.x /= distance;
from.y /= distance;
from.z /= distance;
settings_set_sight_params( map->settings, &from, &up );
}
void map_set_shown_objects( map_t* map, star_t *star_list[],
link_t *link_list[] )
{
if( map->star_list )
free( map->star_list );
map->star_list = star_list;
if( map->link_list )
{
int i;
for( i = 0; map->link_list[i]; i++ )
link_destroy( map->link_list[i] );
free( map->link_list );
}
map->link_list = link_list;
build_indexes( map );
draw( map );
}
/* Destroy the map and related data structures. */
void map_destroy( map_t* map )
{
map_widget_destroy( map->map_widget );
free( map->link_list );
if( map->link_index )
link_index_destroy( map->link_index );
free( map->star_list );
if( map->star_index )
star_index_destroy( map->star_index );
perspective_destroy( map->persp );
free( map );
}
/* Private Functions */
void build_indexes( map_t *map )
{
/* If the old one is not NULL destroy it. */
if( map->star_index )
{
star_index_destroy( map->star_index );
map->star_index = NULL;
}
/* Do not try to build the index if we have no stars yet */
if( map->star_list )
map->star_index = star_index_new( map->persp, map->star_list );
if( map->link_index )
{
link_index_destroy( map->link_index );
map->link_index = NULL;
}
if( map->link_list )
map->link_index = link_index_new( map->persp, map->link_list );
}
void draw( map_t *map )
{
map_widget_clear( map->map_widget );
if( map->link_index && settings_get_show_links(map->settings) )
link_index_foreach( map->link_index,
(void(*)(link_t*, double, double,
double, double, void*))draw_link,
map );
if( map->star_index )
star_index_foreach( map->star_index,
(void(*)(star_t*, double, double, void*))
draw_star, map );
map_widget_show( map->map_widget );
}
void draw_star( star_t* star, double x, double y, map_t* map )
{
int show_name, radius;
double rgb[3];
char name[30]; /* FIXME: This size should be defined elsewhere. */
settings_find_star_draw( map->settings, star, &radius,
rgb, &show_name );
map_widget_draw_point( map->map_widget, x, y, radius, rgb );
if( show_name && settings_get_show_star_labels( map->settings ) )
{
settings_get_labels_color( map->settings, rgb );
star_get_short_name( star, name );
map_widget_draw_label( map->map_widget, x, y, radius,
map->font, rgb, name );
}
}
void draw_link( link_t* link, double x1, double y1,
double x2, double y2, map_t* map )
{
int width;
GdkLineStyle style;
double rgb[3];
double distance, x, y;
char label[5];
distance_unit_t unit;
settings_find_link_draw( map->settings, link, &width, &style, rgb );
map_widget_draw_line( map->map_widget, x1, y1, x2, y2, width,
rgb, style );
if( settings_get_show_link_labels( map->settings ) )
{
distance = link_get_distance( link );
unit = settings_get_distance_unit( map->settings );
sprintf( label, "%5.1f", (unit==DISTANCE_PARSECS) ? distance :
distance*PARSEC_TO_LY );
x = (x1+x2)/2;
y = (y1+y2)/2;
settings_get_labels_color( map->settings, rgb );
map_widget_draw_label( map->map_widget, x, y, 0, map->font,
rgb, label );
}
}
void cb_clicked_on_map( map_widget_t* map_widget, double x, double y,
double lambda, map_t *map )
{
star_t *star;
/* If there is no loaded map, just ignore the click */
if( !map->star_index )
return;
map->lambda = lambda;
map->clicked_x = x;
map->clicked_y = y;
star = star_index_find_first( map->star_index,
(int(*)(star_t*,double,double,void*))
cb_is_clicked_star,
map, STAR_INDEX_START_BEGIN );
if( star )
map->star_clicked( star, map->star_clicked_data );
}
void cb_redraw_map( map_widget_t* map_widget, map_t *map )
{
draw( map );
}
void cb_rotate_map( map_widget_t* map_widget, double v_ang,
double h_ang, map_t *map )
{
map_rotate( map, v_ang, h_ang );
}
void cb_view_radius(settings_t* settings, map_t *map)
{
/* The radius of the map has changed. Therefore we must move closer
* or further from the center, so that the map fills the window. */
double view_radius = settings_get_view_radius( settings );
double distance;
coords_3d_t up, from, center;
distance = sqrt( pow( view_radius, 2 ) +
pow( view_radius * (cos(THETA/2)/sin(THETA/2)), 2 ) );
settings_get_sight_params( settings, &from, &up );
settings_get_center( settings, ¢er );
from.x *= distance;
from.y *= distance;
from.z *= distance;
from.x += center.x;
from.y += center.y;
from.z += center.z;
if( map->persp )
perspective_destroy( map->persp );
map->persp = perspective_new( &from, ¢er, &up );
build_indexes(map);
draw( map );
}
void cb_drawing_settings(settings_t* settings, map_t *map)
{
if( map->font )
gdk_font_unref( map->font );
map->font = gdk_font_load(settings_get_labels_font(settings));
draw( map );
}
void cb_sight_params(settings_t* settings, map_t *map)
{
coords_3d_t from, center, up;
double distance, view_radius;
/* We start by checking that we are not the cause of the callback */
if( !map->changed_sight )
{
view_radius = settings_get_view_radius(settings);
settings_get_center( settings, ¢er );
settings_get_sight_params( settings, &from, &up );
distance = sqrt( pow( view_radius, 2 ) +
pow( view_radius * (cos(THETA/2)/sin(THETA/2))
, 2 ) );
from.x *= distance;
from.y *= distance;
from.z *= distance;
if( map->persp )
perspective_destroy(map->persp);
map->persp = perspective_new( &from, ¢er, &up );
build_indexes(map);
draw(map);
}
else
map->changed_sight = 0;
}
void cb_center(settings_t* settings, map_t *map)
{
coords_3d_t center;
settings_get_center( settings, ¢er );
perspective_change_center( map->persp, ¢er );
build_indexes(map);
draw( map );
}
int cb_is_clicked_star(star_t* star, double x, double y, map_t *map )
{
int radius, show_name, is_clicked;
double rgb[3], star_x, star_y;
settings_find_star_draw( map->settings, star, &radius,
rgb, &show_name );
star_x = x*(map->lambda);
star_y = y*(map->lambda);
star_x = abs( map->clicked_x - star_x );
star_y = abs( map->clicked_y - star_y );
is_clicked = ( star_x < (radius+1) ) && ( star_y < (radius+1) );
return is_clicked;
}
syntax highlighted by Code2HTML, v. 0.9.1