2 * Copyright (C) 2008 Liam Girdwood
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330,
17 * Boston, MA 02111-1307, USA.
20 #define _GNU_SOURCE /* for NAN */
25 #include <gdk/gdkkeysyms.h>
26 #include <libnova/utility.h>
27 #include <libnova/transform.h>
28 #include <libnova/julian_day.h>
29 #include <libnova/ln_types.h>
30 #include <libastrodb/astrodb.h>
37 #include "constellation.h"
39 #include "solar_system.h"
42 #define NOVA_SKY_GET_PRIVATE(obj) \
43 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NOVA_TYPE_SKY, Sky))
45 G_DEFINE_TYPE (GtkSky
, virtual_sky
, GTK_TYPE_DRAWING_AREA
);
47 #define SKY_MOVE_SIZE 15
48 #define SKY_UPDATE_MAG_DIFF 1
49 #define SKY_MAG_SCALER 8
50 #define SKY_ZOOM_OUT 1.1
51 #define SKY_ZOOM_IN 0.9
52 #define SKY_MAX_RA 360.0
54 static gboolean
virtual_sky_expose (GtkWidget
*sky_widget
, GdkEventExpose
*event
);
55 static gboolean
virtual_sky_update (gpointer data
);
56 static GdkCursor
*sky_cursor_arrow
;
57 static GdkCursor
*sky_cursor_ptr
;
59 static inline void sky_get_posn(Sky
*sky
, struct render_coord
*coord
)
61 sky
->projection
.trans
->proj_to_sky_equ(&sky
->projection
, coord
);
64 static inline struct astrodb_object
*
65 virtual_sky_get_object (Sky
*sky
, struct render_coord
* rc
)
67 return tile_get_object_at(sky
->tile
, rc
);
70 static gboolean
sky_complete_scroll_render(gpointer data
)
72 GtkWidget
*widget
= data
;
73 Sky
*sky
= NOVA_SKY_GET_PRIVATE(widget
);
75 /* redraw still pending ? */
76 if (sky
->redraw_pending
)
79 sky
->robject
.type
= RT_FULL
;
80 gtk_widget_queue_draw(widget
);
84 static void sky_move_abs_posn(Sky
*sky
, gfloat ra
, gfloat dec
)
86 sky
->projection
.centre_ra
= ra
;
87 sky
->projection
.centre_dec
= dec
;
88 projection_check_bounds(&sky
->projection
);
91 static void sky_zoom(Sky
*sky
, gfloat zoom
)
93 sky
->projection
.fov
*= zoom
;
94 projection_check_bounds(&sky
->projection
);
95 legend_update_fov(sky
->legend_widget
, sky
->projection
.fov
,
96 sky
->projection
.clip_mag_faint
);
99 static gboolean
sky_key_press_event(GtkWidget
*widget
, GdkEventKey
*event
,
102 Sky
*sky
= NOVA_SKY_GET_PRIVATE(widget
);
104 switch (event
->keyval
) {
106 projection_move_rel_pixels(&sky
->projection
, -SKY_MOVE_SIZE
, 0);
109 projection_move_rel_pixels(&sky
->projection
, SKY_MOVE_SIZE
, 0);
112 projection_move_rel_pixels(&sky
->projection
, 0, -SKY_MOVE_SIZE
);
115 projection_move_rel_pixels(&sky
->projection
, 0, SKY_MOVE_SIZE
);
118 sky_zoom(sky
, SKY_ZOOM_OUT
);
121 sky_zoom(sky
, SKY_ZOOM_IN
);
127 sky
->robject
.type
= RT_FAST
;
128 gtk_widget_queue_draw(widget
);
129 sky
->is_modified
= 1;
133 static gboolean
sky_key_release_event(GtkWidget
*widget
, GdkEventKey
*event
,
136 Sky
*sky
= NOVA_SKY_GET_PRIVATE(widget
);
138 sky
->robject
.type
= RT_FULL
;
139 gtk_widget_queue_draw(widget
);
143 static gboolean
sky_enter_notify_event(GtkWidget
*widget
,
144 GdkEventCrossing
*event
,
147 gtk_widget_grab_focus(widget
);
148 gdk_window_set_cursor(event
->window
, sky_cursor_ptr
);
152 static gboolean
sky_leave_notify_event(GtkWidget
*widget
,
153 GdkEventCrossing
*event
,
156 gdk_window_set_cursor(event
->window
, sky_cursor_arrow
);
160 static gboolean
sky_button_press_event(GtkWidget
*widget
,
161 GdkEventButton
*event
,
164 Sky
*sky
= NOVA_SKY_GET_PRIVATE(widget
);
165 GdkModifierType state
;
167 switch (event
->button
) {
169 gdk_window_get_pointer (event
->window
, &sky
->pointer_x
,
170 &sky
->pointer_y
, &state
);
171 sky
->robject
.type
= RT_FAST
;
172 sky
->pointer_drag
= 1;
179 static gboolean
sky_button_release_event(GtkWidget
*widget
,
180 GdkEventButton
*event
,
183 Sky
*sky
= NOVA_SKY_GET_PRIVATE(widget
);
185 switch (event
->button
) {
187 sky
->robject
.type
= RT_FULL
;
188 sky
->pointer_drag
= 0;
189 gtk_widget_queue_draw(widget
);
196 static gboolean
sky_scroll_event(GtkWidget
*widget
,
197 GdkEventScroll
*event
,
200 Sky
*sky
= NOVA_SKY_GET_PRIVATE(widget
);
202 switch (event
->direction
) {
204 sky_zoom(sky
, SKY_ZOOM_IN
);
206 case GDK_SCROLL_DOWN
:
207 sky_zoom(sky
, SKY_ZOOM_OUT
);
209 case GDK_SCROLL_LEFT
:
210 case GDK_SCROLL_RIGHT
:
214 sky
->robject
.type
= RT_FAST
;
215 sky
->redraw_pending
= 1;
216 sky
->is_modified
= 1;
217 gtk_widget_queue_draw(widget
);
219 /* add timeout to draw detail when scroll completes */
220 g_timeout_add(20, sky_complete_scroll_render
, widget
);
224 static gboolean
sky_motion_notify_event(GtkWidget
*widget
,
225 GdkEventMotion
*event
,
228 GdkModifierType state
;
229 struct render_coord rc
;
230 struct ln_equ_posn pos
;
231 struct astrodb_object
*object
;
233 Sky
*sky
= NOVA_SKY_GET_PRIVATE(widget
);
235 sky
->robject
.coord
[0].posn
= &pos
;
236 gdk_window_get_pointer(event
->window
, &x
, &y
, &state
);
238 /* drag the sky to pointer position */
239 if (sky
->pointer_drag
) {
240 projection_move_rel_pixels(&sky
->projection
,
245 gtk_widget_queue_draw(widget
);
246 sky
->is_modified
= 1;
249 /* update the legend position */
250 if (event
->is_hint
) {
251 sky
->robject
.coord
[0].x
= rc
.x
= x
;
252 sky
->robject
.coord
[0].y
= rc
.y
= y
;
253 sky_get_posn(sky
, &sky
->robject
.coord
[0]);
254 legend_update_pointer(sky
->legend_widget
,
255 sky
->robject
.coord
[0].posn
->ra
,
256 sky
->robject
.coord
[0].posn
->dec
);
258 /* update or clear legend object */
259 object
= tile_get_object_at(sky
->tile
, &rc
);
260 legend_set_object(sky
->legend_widget
, object
);
266 void on_sky_markers_activate(GtkMenuItem
*menuitem
, gpointer user_data
)
268 GtkWidget
*dlg
, *sky_widget
= (GtkWidget
*)user_data
;
269 Sky
*sky
= NOVA_SKY_GET_PRIVATE(sky_widget
);
272 dlg
= ui_get_dialog("SkyMarkersDialog");
276 ui_markers_dlg_init(dlg
, &sky
->marker_settings
);
279 result
= gtk_dialog_run(GTK_DIALOG(dlg
));
280 if (result
== GTK_RESPONSE_OK
|| result
== GTK_RESPONSE_APPLY
)
281 gtk_widget_queue_draw(sky_widget
);
282 } while (result
== GTK_RESPONSE_APPLY
|| result
== GTK_RESPONSE_HELP
);
284 gtk_widget_destroy(dlg
);
287 static int render_stars(gpointer data
, gpointer udata
)
290 struct astrodb_slist
*objects
= data
;
291 struct sky2kv4_object
*star
;
294 /* get object --> transform --> render_object */
295 star
= (struct sky2kv4_object
*)objects
->data
;
297 sky
->robject
.object
= star
;
299 if (star
->object
.posn_mag
.Vmag
== FP_NAN
)
302 sky
->robject
.coord
[0].posn
= &star
->object
.posn_mag
.ra
;
303 sky
->projection
.trans
->sky_to_proj_equ(&sky
->projection
,
304 &sky
->robject
.coord
[0]);
305 if (projection_is_visible0(&sky
->projection
, &sky
->robject
)) {
307 star_object_render(&sky
->robject
);
308 if (sky
->robject
.type
== RT_FULL
)
309 tile_add_object(sky
->tile
, &sky
->robject
);
312 objects
= astrodb_slist_next(objects
);
317 static void render_default(Sky
*sky
)
319 gfloat mag_bright
, mag_faint
, mag_index
;
320 struct astrodb_slist
*objects
= NULL
;
322 /* are we only rendering bright objects */
323 if (sky
->robject
.type
== RT_FAST
)
324 mag_faint
= sky
->projection
.clip_mag_faint
-
327 mag_faint
= sky
->projection
.clip_mag_faint
;
329 mag_bright
= sky
->projection
.clip_mag_bright
;
331 /* render_object in magnitude bands from
332 * brightest --> faintest_magnitude */
333 for (mag_index
= mag_bright
; mag_index
< mag_faint
; mag_index
++) {
335 /* clip the catalog in mag bands */
336 astrodb_table_unclip(sky
->default_star_table
);
337 astrodb_table_clip_on_fov(sky
->default_star_table
,
338 sky
->projection
.centre_az
,
339 sky
->projection
.centre_alt
,
341 mag_index
, mag_index
-1);
343 if (astrodb_table_get_objects(sky
->default_star_table
,
344 &objects
, ADB_SMEM
) == 0)
347 astrodb_slist_foreach(objects
, render_stars
, sky
);
348 astrodb_table_put_objects(objects
);
354 * Render the virtual sky
356 * There are 2 different render_object types:-
358 * RT_FULL --> render_object everthing that is visible
359 * RT_FAST --> render_object bright items that are visible
361 static void vsky_render(Sky
*sky
)
363 /* render_object every item that is visible */
364 if (sky
->marker_settings
.show_const_figures
)
365 constellation_render_lines(sky
);
367 if (sky
->marker_settings
.show_grid
)
368 markers_grid_render(sky
);
370 if (sky
->marker_settings
.show_const_bounds
)
371 constellation_render_bounds(sky
);
372 if (sky
->marker_settings
.show_const_names
)
373 constellation_render_names(sky
);
375 if (sky
->marker_settings
.show_horizon
)
376 markers_horizon_render(sky
);
377 if (sky
->marker_settings
.show_horizon_NEWS
)
378 markers_horizon_NEWS_render(sky
);
379 if (sky
->marker_settings
.show_ecliptic
)
380 markers_ecliptic_render(sky
);
381 if (sky
->marker_settings
.show_galactic_poles
)
382 markers_galactic_poles_render(sky
);
384 if (sky
->solar_system_settings
.planets
.show_object
)
386 if (sky
->solar_system_settings
.solar
.show_object
)
388 if (sky
->solar_system_settings
.lunar
.show_object
)
395 static void virtual_sky_class_init (GtkSkyClass
*class)
397 GObjectClass
*obj_class
;
398 GtkWidgetClass
*widget_class
;
400 obj_class
= G_OBJECT_CLASS (class);
401 widget_class
= GTK_WIDGET_CLASS (class);
403 widget_class
->expose_event
= virtual_sky_expose
;
405 g_type_class_add_private (obj_class
, sizeof(Sky
));
408 static void virtual_sky_init (GtkSky
*gtk_sky
)
410 Sky
*sky
= NOVA_SKY_GET_PRIVATE(gtk_sky
);
412 sky
->default_star_table
= db_get_default_table("star");
413 projection_set_flip(&sky
->projection
, PF_NONE
);
414 sky
->projection
.centre_ra
= 0;
415 sky
->projection
.centre_dec
= 0;
416 sky
->projection
.fov
= 180;
417 sky
->projection
.clip_mag_faint
= 3;
418 sky
->projection
.clip_mag_bright
= -2;
419 sky
->projection
.coords
= PC_ALT_AZ
;
420 sky
->tile
= tile_init();
422 sky
->marker_settings
.show_const_figures
= 1;
423 sky
->marker_settings
.show_const_bounds
= 1;
424 sky
->marker_settings
.show_grid
= 1;
425 sky
->marker_settings
.show_grid_labels
= 1;
426 sky
->marker_settings
.show_horizon
= 1;
427 sky
->marker_settings
.show_horizon_NEWS
= 1;
428 sky
->solar_system_settings
.planets
.show_object
= 1;
429 sky
->solar_system_settings
.planets
.show_label
= 1;
430 sky
->solar_system_settings
.lunar
.show_label
= 1;
431 sky
->solar_system_settings
.lunar
.show_object
= 1;
432 sky
->solar_system_settings
.solar
.show_label
= 1;
433 sky
->solar_system_settings
.solar
.show_object
= 1;
435 sky
->robject
.type
= RT_FULL
;
437 // TODO: get observer location and JD 1st run
438 sky
->observer
.JD
= ln_get_julian_from_sys();
439 sky
->observer
.posn
.lng
= 3.0;
440 sky
->observer
.posn
.lat
= 56.0;
441 projection_set_observer(&sky
->projection
, &sky
->observer
);
443 virtual_sky_update (gtk_sky
);
445 /* update the vsky once a second - TODO vary time based on FOV */
446 g_timeout_add(1000, virtual_sky_update
, gtk_sky
);
449 static void sky_draw(GtkWidget
*sky_widget
, cairo_t
*cr
)
451 Sky
*sky
= NOVA_SKY_GET_PRIVATE(sky_widget
);
452 GTimeVal start
, finish
;
454 g_get_current_time(&start
);
456 tile_set_array_size(sky
->tile
, sky_widget
->allocation
.width
,
457 sky_widget
->allocation
.height
);
458 tile_reset_array(sky
->tile
);
460 projection_fov_bounds(&sky
->projection
, &sky
->observer
);
461 sky
->robject
.context
.pixels_per_degree
=
462 sky
->projection
.pixels_per_degree
;
463 sky
->robject
.context
.JD
= sky
->observer
.JD
;
464 sky
->robject
.context
.faintest_magnitude
=
465 sky
->projection
.clip_mag_faint
;
467 sky
->robject
.cr
= cr
;
471 sky
->redraw_pending
= sky
->is_modified
= 0;
472 g_get_current_time(&finish
);
473 //printf("finish %ld fps %ld\n", finish.tv_usec - start.tv_usec,
474 // 1000000 / (finish.tv_usec - start.tv_usec));
477 static gboolean
virtual_sky_expose (GtkWidget
*sky_widget
,
478 GdkEventExpose
*event
)
481 Sky
*sky
= NOVA_SKY_GET_PRIVATE(sky_widget
);
483 /* the sky is modified if we have been resized */
484 if (sky
->projection
.sky_width
!= sky_widget
->allocation
.width
||
485 sky
->projection
.sky_height
!= sky_widget
->allocation
.height
)
486 sky
->is_modified
= 1;
488 /* save sky dimensions */
489 sky
->projection
.sky_width
= sky_widget
->allocation
.width
;
490 sky
->projection
.sky_height
= sky_widget
->allocation
.height
;
493 cr
= gdk_cairo_create(sky_widget
->window
);
495 /* draw night sky background */
496 cairo_set_source_rgb(cr
, 0, 0, 0);
499 /* render_object sky objects */
500 sky_draw(sky_widget
, cr
);
506 static void virtual_sky_redraw_canvas (GtkSky
*gtk_sky
)
511 widget
= GTK_WIDGET(gtk_sky
);
516 region
= gdk_drawable_get_clip_region(widget
->window
);
517 /* redraw the cairo canvas completely by exposing it */
518 gdk_window_invalidate_region(widget
->window
, region
, TRUE
);
519 gdk_window_process_updates(widget
->window
, TRUE
);
521 gdk_region_destroy(region
);
524 static gboolean
virtual_sky_update (gpointer data
)
526 GtkSky
*gtk_sky
= NOVA_SKY(data
);
527 Sky
*sky
= NOVA_SKY_GET_PRIVATE(gtk_sky
);
529 sky
->observer
.JD
= ln_get_julian_from_sys();
530 virtual_sky_redraw_canvas(gtk_sky
);
532 return TRUE
; /* keep running this event */
535 GtkWidget
* virtual_sky_new (void)
537 return g_object_new(NOVA_TYPE_SKY
, NULL
);
541 void sky_connect_signals(GtkWidget
*gtk_sky
)
543 g_signal_connect(gtk_sky
, "key_press_event",
544 G_CALLBACK (sky_key_press_event
), NULL
);
545 g_signal_connect(gtk_sky
, "key_release_event",
546 G_CALLBACK (sky_key_release_event
), NULL
);
547 g_signal_connect(gtk_sky
, "button_press_event",
548 G_CALLBACK (sky_button_press_event
), NULL
);
549 g_signal_connect(gtk_sky
, "button_release_event",
550 G_CALLBACK (sky_button_release_event
), NULL
);
551 g_signal_connect(gtk_sky
, "motion_notify_event",
552 G_CALLBACK (sky_motion_notify_event
), NULL
);
553 g_signal_connect(gtk_sky
, "enter_notify_event",
554 G_CALLBACK (sky_enter_notify_event
), NULL
);
555 g_signal_connect(gtk_sky
, "leave_notify_event",
556 G_CALLBACK (sky_leave_notify_event
), NULL
);
557 g_signal_connect(gtk_sky
, "scroll_event",
558 G_CALLBACK (sky_scroll_event
), NULL
);
560 g_object_set(gtk_sky
, "can-default", TRUE
, "can-focus", TRUE
, NULL
);
561 gtk_widget_add_events (gtk_sky
, GDK_POINTER_MOTION_MASK
|
562 GDK_POINTER_MOTION_HINT_MASK
|
563 GDK_BUTTON_PRESS_MASK
|
564 GDK_BUTTON_RELEASE_MASK
|
566 GDK_KEY_RELEASE_MASK
|
567 GDK_ENTER_NOTIFY_MASK
|
568 GDK_LEAVE_NOTIFY_MASK
);
570 sky_cursor_arrow
= gdk_cursor_new(GDK_LEFT_PTR
);
571 sky_cursor_ptr
= gdk_cursor_new(GDK_CROSSHAIR
);
574 void sky_set_legend(GtkWidget
*sky_widget
, GtkWidget
*legend_widget
)
576 Sky
*sky
= NOVA_SKY_GET_PRIVATE(sky_widget
);
578 sky
->legend_widget
= legend_widget
;
579 legend_set_observer(sky
->legend_widget
, &sky
->observer
);