optimizations: general renderering speedups.
[nova.git] / src / sky / sky.c
blob9ce5bdb9bf0bdf5da4ab873db300698bed1f7f5c
1 /*
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 */
22 #include <math.h>
23 #include <time.h>
24 #include <gtk/gtk.h>
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>
31 #include "sky.h"
32 #include "render.h"
33 #include "star.h"
34 #include "tile.h"
35 #include "db.h"
36 #include "legend.h"
37 #include "constellation.h"
38 #include "grid.h"
39 #include "solar_system.h"
40 #include "glade.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)
77 return TRUE;
79 sky->robject.type = RT_FULL;
80 gtk_widget_queue_draw(widget);
81 return FALSE;
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,
100 gpointer user_data)
102 Sky *sky = NOVA_SKY_GET_PRIVATE(widget);
104 switch (event->keyval) {
105 case GDK_Right:
106 projection_move_rel_pixels(&sky->projection, -SKY_MOVE_SIZE, 0);
107 break;
108 case GDK_Left:
109 projection_move_rel_pixels(&sky->projection, SKY_MOVE_SIZE, 0);
110 break;
111 case GDK_Down:
112 projection_move_rel_pixels(&sky->projection, 0, -SKY_MOVE_SIZE);
113 break;
114 case GDK_Up:
115 projection_move_rel_pixels(&sky->projection, 0, SKY_MOVE_SIZE);
116 break;
117 case GDK_minus:
118 sky_zoom(sky, SKY_ZOOM_OUT);
119 break;
120 case GDK_equal:
121 sky_zoom(sky, SKY_ZOOM_IN);
122 break;
123 default:
124 return TRUE;
127 sky->robject.type = RT_FAST;
128 gtk_widget_queue_draw(widget);
129 sky->is_modified = 1;
130 return TRUE;
133 static gboolean sky_key_release_event(GtkWidget *widget, GdkEventKey *event,
134 gpointer user_data)
136 Sky *sky = NOVA_SKY_GET_PRIVATE(widget);
138 sky->robject.type = RT_FULL;
139 gtk_widget_queue_draw(widget);
140 return TRUE;
143 static gboolean sky_enter_notify_event(GtkWidget *widget,
144 GdkEventCrossing *event,
145 gpointer user_data)
147 gtk_widget_grab_focus(widget);
148 gdk_window_set_cursor(event->window, sky_cursor_ptr);
149 return TRUE;
152 static gboolean sky_leave_notify_event(GtkWidget *widget,
153 GdkEventCrossing *event,
154 gpointer user_data)
156 gdk_window_set_cursor(event->window, sky_cursor_arrow);
157 return TRUE;
160 static gboolean sky_button_press_event(GtkWidget *widget,
161 GdkEventButton *event,
162 gpointer user_data)
164 Sky *sky = NOVA_SKY_GET_PRIVATE(widget);
165 GdkModifierType state;
167 switch (event->button) {
168 case 1:
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;
173 break;
176 return TRUE;
179 static gboolean sky_button_release_event(GtkWidget *widget,
180 GdkEventButton *event,
181 gpointer user_data)
183 Sky *sky = NOVA_SKY_GET_PRIVATE(widget);
185 switch (event->button) {
186 case 1:
187 sky->robject.type = RT_FULL;
188 sky->pointer_drag = 0;
189 gtk_widget_queue_draw(widget);
190 break;
193 return TRUE;
196 static gboolean sky_scroll_event(GtkWidget *widget,
197 GdkEventScroll *event,
198 gpointer user_data)
200 Sky *sky = NOVA_SKY_GET_PRIVATE(widget);
202 switch (event->direction) {
203 case GDK_SCROLL_UP:
204 sky_zoom(sky, SKY_ZOOM_IN);
205 break;
206 case GDK_SCROLL_DOWN:
207 sky_zoom(sky, SKY_ZOOM_OUT);
208 break;
209 case GDK_SCROLL_LEFT:
210 case GDK_SCROLL_RIGHT:
211 break;
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);
221 return TRUE;
224 static gboolean sky_motion_notify_event(GtkWidget *widget,
225 GdkEventMotion *event,
226 gpointer user_data)
228 GdkModifierType state;
229 struct render_coord rc;
230 struct ln_equ_posn pos;
231 struct astrodb_object *object;
232 gint x,y;
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,
241 x - sky->pointer_x,
242 y - sky->pointer_y);
243 sky->pointer_x = x;
244 sky->pointer_y = y;
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);
263 return TRUE;
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);
270 gint result = 0;
272 dlg = ui_get_dialog("SkyMarkersDialog");
273 if (dlg == NULL)
274 return;
276 ui_markers_dlg_init(dlg, &sky->marker_settings);
278 do {
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)
289 Sky *sky = udata;
290 struct astrodb_slist *objects = data;
291 struct star_object *star;
293 while (objects) {
294 /* get object --> transform --> render_object */
295 star = (struct star_object*)objects->data;
297 sky->robject.object = star;
299 if (star->dobject.Vmag == FP_NAN)
300 goto next;
302 sky->robject.coord[0].posn = &star->dobject.posn;
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);
311 next:
312 objects = astrodb_slist_next(objects);
314 return 0;
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 -
325 SKY_UPDATE_MAG_DIFF;
326 else
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,
340 sky->projection.fov,
341 mag_index, mag_index -1);
343 if (astrodb_table_get_objects(sky->default_star_table,
344 &objects, ADB_SMEM) == 0)
345 continue;
347 astrodb_slist_foreach(objects, render_stars, sky);
348 astrodb_table_put_objects(objects);
349 objects = NULL;
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)
385 planets_render(sky);
386 if (sky->solar_system_settings.solar.show_object)
387 solar_render(sky);
388 if (sky->solar_system_settings.lunar.show_object)
389 lunar_render(sky);
391 render_default(sky);
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 */
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;
468 vsky_render(sky);
470 cairo_restore(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)
480 cairo_t *cr;
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;
492 /* get a cairo_t */
493 cr = gdk_cairo_create(sky_widget->window);
495 /* draw night sky background */
496 cairo_set_source_rgb(cr, 0, 0, 0);
497 cairo_paint(cr);
499 /* render_object sky objects */
500 sky_draw(sky_widget, cr);
501 cairo_destroy(cr);
503 return FALSE;
506 static void virtual_sky_redraw_canvas (GtkSky *gtk_sky)
508 GtkWidget *widget;
509 GdkRegion *region;
511 widget = GTK_WIDGET(gtk_sky);
513 if (!widget->window)
514 return;
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);
528 time_t timet;
530 /* update the time */
531 time(&timet);
532 localtime_r(&timet, &sky->time);
534 virtual_sky_redraw_canvas(gtk_sky);
536 return TRUE; /* keep running this event */
539 GtkWidget * virtual_sky_new (void)
541 return g_object_new(NOVA_TYPE_SKY, NULL);
545 void sky_connect_signals(GtkWidget *gtk_sky)
547 g_signal_connect(gtk_sky, "key_press_event",
548 G_CALLBACK (sky_key_press_event), NULL);
549 g_signal_connect(gtk_sky, "key_release_event",
550 G_CALLBACK (sky_key_release_event), NULL);
551 g_signal_connect(gtk_sky, "button_press_event",
552 G_CALLBACK (sky_button_press_event), NULL);
553 g_signal_connect(gtk_sky, "button_release_event",
554 G_CALLBACK (sky_button_release_event), NULL);
555 g_signal_connect(gtk_sky, "motion_notify_event",
556 G_CALLBACK (sky_motion_notify_event), NULL);
557 g_signal_connect(gtk_sky, "enter_notify_event",
558 G_CALLBACK (sky_enter_notify_event), NULL);
559 g_signal_connect(gtk_sky, "leave_notify_event",
560 G_CALLBACK (sky_leave_notify_event), NULL);
561 g_signal_connect(gtk_sky, "scroll_event",
562 G_CALLBACK (sky_scroll_event), NULL);
564 g_object_set(gtk_sky, "can-default", TRUE, "can-focus", TRUE, NULL);
565 gtk_widget_add_events (gtk_sky, GDK_POINTER_MOTION_MASK |
566 GDK_POINTER_MOTION_HINT_MASK |
567 GDK_BUTTON_PRESS_MASK |
568 GDK_BUTTON_RELEASE_MASK |
569 GDK_KEY_PRESS_MASK |
570 GDK_KEY_RELEASE_MASK |
571 GDK_ENTER_NOTIFY_MASK |
572 GDK_LEAVE_NOTIFY_MASK);
574 sky_cursor_arrow = gdk_cursor_new(GDK_LEFT_PTR);
575 sky_cursor_ptr = gdk_cursor_new(GDK_CROSSHAIR);
578 void sky_set_legend(GtkWidget *sky_widget, GtkWidget *legend_widget)
580 Sky *sky = NOVA_SKY_GET_PRIVATE(sky_widget);
582 sky->legend_widget = legend_widget;
583 legend_set_observer(sky->legend_widget, &sky->observer);