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.
23 #include <gdk/gdkkeysyms.h>
24 #include <libnova/utility.h>
25 #include <libnova/transform.h>
26 #include <libnova/julian_day.h>
27 #include <libnova/ln_types.h>
28 #include <libastrodb/astrodb.h>
35 #include "constellation.h"
37 #include "solar_system.h"
40 #define NOVA_SKY_GET_PRIVATE(obj) \
41 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NOVA_TYPE_SKY, Sky))
43 G_DEFINE_TYPE (GtkSky
, virtual_sky
, GTK_TYPE_DRAWING_AREA
);
45 #define SKY_MOVE_SIZE 15
46 #define SKY_UPDATE_MAG_DIFF 1
47 #define SKY_MAG_SCALER 8
48 #define SKY_ZOOM_OUT 1.1
49 #define SKY_ZOOM_IN 0.9
50 #define SKY_MAX_RA 360.0
52 static gboolean
virtual_sky_expose (GtkWidget
*sky_widget
, GdkEventExpose
*event
);
53 static gboolean
virtual_sky_update (gpointer data
);
54 static GdkCursor
*sky_cursor_arrow
;
55 static GdkCursor
*sky_cursor_ptr
;
57 static inline void sky_get_posn(Sky
*sky
, struct render_coord
*coord
)
59 sky
->projection
.trans
->proj_to_sky_equ(&sky
->projection
, coord
);
62 static inline struct astrodb_object
*
63 virtual_sky_get_object (Sky
*sky
, struct render_coord
* rc
)
65 return tile_get_object_at(sky
->tile
, rc
);
68 static gboolean
sky_complete_scroll_render(gpointer data
)
70 GtkWidget
*widget
= data
;
71 Sky
*sky
= NOVA_SKY_GET_PRIVATE(widget
);
73 /* redraw still pending ? */
74 if (sky
->redraw_pending
)
77 sky
->robject
.type
= RT_FULL
;
78 gtk_widget_queue_draw(widget
);
82 static void sky_move_abs_posn(Sky
*sky
, gfloat ra
, gfloat dec
)
84 sky
->projection
.centre_ra
= ra
;
85 sky
->projection
.centre_dec
= dec
;
86 projection_check_bounds(&sky
->projection
);
89 static void sky_zoom(Sky
*sky
, gfloat zoom
)
91 sky
->projection
.fov
*= zoom
;
92 projection_check_bounds(&sky
->projection
);
93 legend_update_fov(sky
->legend_widget
, sky
->projection
.fov
,
94 sky
->projection
.clip_mag_faint
);
97 static gboolean
sky_key_press_event(GtkWidget
*widget
, GdkEventKey
*event
,
100 Sky
*sky
= NOVA_SKY_GET_PRIVATE(widget
);
102 switch (event
->keyval
) {
104 projection_move_rel_pixels(&sky
->projection
, -SKY_MOVE_SIZE
, 0);
107 projection_move_rel_pixels(&sky
->projection
, SKY_MOVE_SIZE
, 0);
110 projection_move_rel_pixels(&sky
->projection
, 0, -SKY_MOVE_SIZE
);
113 projection_move_rel_pixels(&sky
->projection
, 0, SKY_MOVE_SIZE
);
116 sky_zoom(sky
, SKY_ZOOM_OUT
);
119 sky_zoom(sky
, SKY_ZOOM_IN
);
125 sky
->robject
.type
= RT_FAST
;
126 gtk_widget_queue_draw(widget
);
127 sky
->is_modified
= 1;
131 static gboolean
sky_key_release_event(GtkWidget
*widget
, GdkEventKey
*event
,
134 Sky
*sky
= NOVA_SKY_GET_PRIVATE(widget
);
136 sky
->robject
.type
= RT_FULL
;
137 gtk_widget_queue_draw(widget
);
141 static gboolean
sky_enter_notify_event(GtkWidget
*widget
,
142 GdkEventCrossing
*event
,
145 gtk_widget_grab_focus(widget
);
146 gdk_window_set_cursor(event
->window
, sky_cursor_ptr
);
150 static gboolean
sky_leave_notify_event(GtkWidget
*widget
,
151 GdkEventCrossing
*event
,
154 gdk_window_set_cursor(event
->window
, sky_cursor_arrow
);
158 static gboolean
sky_button_press_event(GtkWidget
*widget
,
159 GdkEventButton
*event
,
162 Sky
*sky
= NOVA_SKY_GET_PRIVATE(widget
);
163 GdkModifierType state
;
165 switch (event
->button
) {
167 gdk_window_get_pointer (event
->window
, &sky
->pointer_x
,
168 &sky
->pointer_y
, &state
);
169 sky
->robject
.type
= RT_FAST
;
170 sky
->pointer_drag
= 1;
177 static gboolean
sky_button_release_event(GtkWidget
*widget
,
178 GdkEventButton
*event
,
181 Sky
*sky
= NOVA_SKY_GET_PRIVATE(widget
);
183 switch (event
->button
) {
185 sky
->robject
.type
= RT_FULL
;
186 sky
->pointer_drag
= 0;
187 gtk_widget_queue_draw(widget
);
194 static gboolean
sky_scroll_event(GtkWidget
*widget
,
195 GdkEventScroll
*event
,
198 Sky
*sky
= NOVA_SKY_GET_PRIVATE(widget
);
200 switch (event
->direction
) {
202 sky_zoom(sky
, SKY_ZOOM_IN
);
204 case GDK_SCROLL_DOWN
:
205 sky_zoom(sky
, SKY_ZOOM_OUT
);
207 case GDK_SCROLL_LEFT
:
208 case GDK_SCROLL_RIGHT
:
212 sky
->robject
.type
= RT_FAST
;
213 sky
->redraw_pending
= 1;
214 sky
->is_modified
= 1;
215 gtk_widget_queue_draw(widget
);
217 /* add timeout to draw detail when scroll completes */
218 g_timeout_add(20, sky_complete_scroll_render
, widget
);
222 static gboolean
sky_motion_notify_event(GtkWidget
*widget
,
223 GdkEventMotion
*event
,
226 GdkModifierType state
;
227 struct render_coord rc
;
228 struct ln_equ_posn pos
;
229 struct astrodb_object
*object
;
231 Sky
*sky
= NOVA_SKY_GET_PRIVATE(widget
);
233 sky
->robject
.coord
[0].posn
= &pos
;
234 gdk_window_get_pointer(event
->window
, &x
, &y
, &state
);
236 /* drag the sky to pointer position */
237 if (sky
->pointer_drag
) {
238 projection_move_rel_pixels(&sky
->projection
,
243 gtk_widget_queue_draw(widget
);
244 sky
->is_modified
= 1;
247 /* update the legend position */
248 if (event
->is_hint
) {
249 sky
->robject
.coord
[0].x
= rc
.x
= x
;
250 sky
->robject
.coord
[0].y
= rc
.y
= y
;
251 sky_get_posn(sky
, &sky
->robject
.coord
[0]);
252 legend_update_pointer(sky
->legend_widget
,
253 sky
->robject
.coord
[0].posn
->ra
,
254 sky
->robject
.coord
[0].posn
->dec
);
256 /* update or clear legend object */
257 object
= tile_get_object_at(sky
->tile
, &rc
);
258 legend_set_object(sky
->legend_widget
, object
);
264 void on_sky_markers_activate(GtkMenuItem
*menuitem
, gpointer user_data
)
266 GtkWidget
*dlg
, *sky_widget
= (GtkWidget
*)user_data
;
267 Sky
*sky
= NOVA_SKY_GET_PRIVATE(sky_widget
);
270 dlg
= ui_get_dialog("SkyMarkersDialog");
274 ui_markers_dlg_init(dlg
, &sky
->marker_settings
);
277 result
= gtk_dialog_run(GTK_DIALOG(dlg
));
278 if (result
== GTK_RESPONSE_OK
|| result
== GTK_RESPONSE_APPLY
)
279 gtk_widget_queue_draw(sky_widget
);
280 } while (result
== GTK_RESPONSE_APPLY
|| result
== GTK_RESPONSE_HELP
);
282 //app_bar_sync(GTK_WIDGET(menuitem));
283 gtk_widget_destroy(dlg
);
286 static int render_stars(gpointer data
, gpointer udata
)
289 struct astrodb_slist
*objects
= data
;
290 struct star_object
*star
;
293 /* get object --> transform --> render_object */
294 star
= (struct star_object
*)objects
->data
;
296 sky
->robject
.object
= star
;
297 sky
->robject
.coord
[0].posn
= &star
->dobject
.posn
;
298 sky
->projection
.trans
->sky_to_proj_equ(&sky
->projection
,
299 &sky
->robject
.coord
[0]);
301 if (projection_is_visible0(&sky
->projection
, &sky
->robject
)) {
303 star_object_render(&sky
->robject
);
305 if (sky
->robject
.type
== RT_FULL
)
306 tile_add_object(sky
->tile
, &sky
->robject
);
308 objects
= astrodb_slist_next(objects
);
313 static void render_default(Sky
*sky
)
315 gfloat mag_bright
, mag_faint
, mag_index
;
316 struct astrodb_slist
*objects
= NULL
;
318 /* are we only rendering bright objects */
319 if (sky
->robject
.type
== RT_FAST
)
320 mag_faint
= sky
->projection
.clip_mag_faint
-
323 mag_faint
= sky
->projection
.clip_mag_faint
;
325 mag_bright
= sky
->projection
.clip_mag_bright
;
327 /* render_object in magnitude bands from
328 * brightest --> faintest_magnitude */
329 for (mag_index
= mag_bright
; mag_index
< mag_faint
; mag_index
++) {
331 /* clip the catalog in mag bands */
332 astrodb_table_unclip(sky
->default_star_table
);
333 astrodb_table_clip_on_fov(sky
->default_star_table
,
334 sky
->projection
.centre_az
,
335 sky
->projection
.centre_alt
,
337 mag_index
, mag_index
-1);
339 if (astrodb_table_get_objects(sky
->default_star_table
,
340 &objects
, ADB_SMEM
) == 0)
343 astrodb_slist_foreach(objects
, render_stars
, sky
);
344 astrodb_table_put_objects(objects
);
350 * Render the virtual sky
352 * There are 2 different render_object types:-
354 * RT_FULL --> render_object everthing that is visible
355 * RT_FAST --> render_object bright items that are visible
357 static void vsky_render(Sky
*sky
)
359 /* render_object every item that is visible */
360 if (sky
->marker_settings
.show_const_figures
)
361 constellation_render_lines(sky
);
363 if (sky
->marker_settings
.show_grid
)
366 if (sky
->marker_settings
.show_const_bounds
)
367 constellation_render_bounds(sky
);
368 if (sky
->marker_settings
.show_const_names
)
369 constellation_render_names(sky
);
371 /* render_object every item that is visible */
372 if (sky
->solar_system_settings
.planets
.show_object
)
374 if (sky
->solar_system_settings
.solar
.show_object
)
376 if (sky
->solar_system_settings
.lunar
.show_object
)
383 static void virtual_sky_class_init (GtkSkyClass
*class)
385 GObjectClass
*obj_class
;
386 GtkWidgetClass
*widget_class
;
388 obj_class
= G_OBJECT_CLASS (class);
389 widget_class
= GTK_WIDGET_CLASS (class);
391 widget_class
->expose_event
= virtual_sky_expose
;
393 g_type_class_add_private (obj_class
, sizeof(Sky
));
396 static void virtual_sky_init (GtkSky
*gtk_sky
)
398 Sky
*sky
= NOVA_SKY_GET_PRIVATE(gtk_sky
);
400 sky
->default_star_table
= db_get_default_table("star");
401 projection_set_flip(&sky
->projection
, PF_NONE
);
402 sky
->projection
.centre_ra
= 0;
403 sky
->projection
.centre_dec
= 0;
404 sky
->projection
.fov
= 180;
405 sky
->projection
.clip_mag_faint
= 3;
406 sky
->projection
.clip_mag_bright
= -2;
407 sky
->projection
.coords
= PC_ALT_AZ
;
408 sky
->tile
= tile_init();
410 sky
->marker_settings
.show_const_figures
= 1;
411 sky
->marker_settings
.show_grid
= 1;
412 sky
->marker_settings
.show_grid_labels
= 1;
413 sky
->solar_system_settings
.planets
.show_object
= 1;
414 sky
->solar_system_settings
.planets
.show_label
= 1;
415 sky
->solar_system_settings
.lunar
.show_label
= 1;
416 sky
->solar_system_settings
.lunar
.show_object
= 1;
417 sky
->solar_system_settings
.solar
.show_label
= 1;
418 sky
->solar_system_settings
.solar
.show_object
= 1;
420 sky
->robject
.type
= RT_FULL
;
422 // TODO: get observer location and JD 1st run
423 sky
->observer
.JD
= ln_get_julian_from_sys();
424 sky
->observer
.posn
.lng
= 3.0;
425 sky
->observer
.posn
.lat
= 56.0;
426 projection_set_observer(&sky
->projection
, &sky
->observer
);
428 virtual_sky_update (gtk_sky
);
430 /* update the vsky once a second */
431 //g_timeout_add(1000, virtual_sky_update, gtk_sky);
434 static void sky_draw(GtkWidget
*sky_widget
, cairo_t
*cr
)
436 Sky
*sky
= NOVA_SKY_GET_PRIVATE(sky_widget
);
438 tile_set_array_size(sky
->tile
, sky_widget
->allocation
.width
,
439 sky_widget
->allocation
.height
);
440 tile_reset_array(sky
->tile
);
442 projection_fov_bounds(&sky
->projection
, &sky
->observer
);
443 sky
->robject
.context
.pixels_per_degree
=
444 sky
->projection
.pixels_per_degree
;
445 sky
->robject
.context
.JD
= sky
->observer
.JD
;
446 sky
->robject
.context
.faintest_magnitude
=
447 sky
->projection
.clip_mag_faint
;
449 sky
->robject
.cr
= cr
;
452 sky
->redraw_pending
= sky
->is_modified
= 0;
455 static gboolean
virtual_sky_expose (GtkWidget
*sky_widget
,
456 GdkEventExpose
*event
)
459 Sky
*sky
= NOVA_SKY_GET_PRIVATE(sky_widget
);
461 /* the sky is modified if we have been resized */
462 if (sky
->projection
.sky_width
!= sky_widget
->allocation
.width
||
463 sky
->projection
.sky_height
!= sky_widget
->allocation
.height
)
464 sky
->is_modified
= 1;
466 /* save sky dimensions */
467 sky
->projection
.sky_width
= sky_widget
->allocation
.width
;
468 sky
->projection
.sky_height
= sky_widget
->allocation
.height
;
471 cr
= gdk_cairo_create(sky_widget
->window
);
473 /* draw night sky background */
474 cairo_set_source_rgb(cr
, 0, 0, 0);
477 /* render_object sky objects */
478 sky_draw(sky_widget
, cr
);
484 static void virtual_sky_redraw_canvas (GtkSky
*gtk_sky
)
489 widget
= GTK_WIDGET(gtk_sky
);
494 region
= gdk_drawable_get_clip_region(widget
->window
);
495 /* redraw the cairo canvas completely by exposing it */
496 gdk_window_invalidate_region(widget
->window
, region
, TRUE
);
497 gdk_window_process_updates(widget
->window
, TRUE
);
499 gdk_region_destroy(region
);
502 static gboolean
virtual_sky_update (gpointer data
)
504 GtkSky
*gtk_sky
= NOVA_SKY(data
);
505 Sky
*sky
= NOVA_SKY_GET_PRIVATE(gtk_sky
);
508 /* update the time */
510 localtime_r(&timet
, &sky
->time
);
512 virtual_sky_redraw_canvas(gtk_sky
);
514 return TRUE
; /* keep running this event */
517 GtkWidget
* virtual_sky_new (void)
519 return g_object_new(NOVA_TYPE_SKY
, NULL
);
523 void sky_connect_signals(GtkWidget
*gtk_sky
)
525 g_signal_connect(gtk_sky
, "key_press_event",
526 G_CALLBACK (sky_key_press_event
), NULL
);
527 g_signal_connect(gtk_sky
, "key_release_event",
528 G_CALLBACK (sky_key_release_event
), NULL
);
529 g_signal_connect(gtk_sky
, "button_press_event",
530 G_CALLBACK (sky_button_press_event
), NULL
);
531 g_signal_connect(gtk_sky
, "button_release_event",
532 G_CALLBACK (sky_button_release_event
), NULL
);
533 g_signal_connect(gtk_sky
, "motion_notify_event",
534 G_CALLBACK (sky_motion_notify_event
), NULL
);
535 g_signal_connect(gtk_sky
, "enter_notify_event",
536 G_CALLBACK (sky_enter_notify_event
), NULL
);
537 g_signal_connect(gtk_sky
, "leave_notify_event",
538 G_CALLBACK (sky_leave_notify_event
), NULL
);
539 g_signal_connect(gtk_sky
, "scroll_event",
540 G_CALLBACK (sky_scroll_event
), NULL
);
542 g_object_set(gtk_sky
, "can-default", TRUE
, "can-focus", TRUE
, NULL
);
543 gtk_widget_add_events (gtk_sky
, GDK_POINTER_MOTION_MASK
|
544 GDK_POINTER_MOTION_HINT_MASK
|
545 GDK_BUTTON_PRESS_MASK
|
546 GDK_BUTTON_RELEASE_MASK
|
548 GDK_KEY_RELEASE_MASK
|
549 GDK_ENTER_NOTIFY_MASK
|
550 GDK_LEAVE_NOTIFY_MASK
);
552 sky_cursor_arrow
= gdk_cursor_new(GDK_LEFT_PTR
);
553 sky_cursor_ptr
= gdk_cursor_new(GDK_CROSSHAIR
);
556 void sky_set_legend(GtkWidget
*sky_widget
, GtkWidget
*legend_widget
)
558 Sky
*sky
= NOVA_SKY_GET_PRIVATE(sky_widget
);
560 sky
->legend_widget
= legend_widget
;
561 legend_set_observer(sky
->legend_widget
, &sky
->observer
);