sky: grid - fixed grid at low FOV.
[nova.git] / src / sky / grid.c
blob91c642fe8cb37898497be6895944b42b01f627b4
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.
19 * Fast sky grid render engine. Mostly working, although probably needs
20 * checking by a maths trig guru to further optimise (and clip RA line at 80).
22 * TODO: Clip RA grid lines at 80 and -80 on DEC. This needs cairo_arc_to() or
23 * someone who knows another solution.
26 #define _GNU_SOURCE /* for NAN and INF */
28 #include <math.h>
29 #include <stdio.h>
30 #include "astro_object.h"
32 #include "grid.h"
34 #define D2R (1.7453292519943295769e-2) /* deg->radian */
35 #define R2D (5.7295779513082320877e1) /* radian->deg */
37 #define ARC_MAGIC 1000.0 /* need better method to determine line from arc */
38 #define GRID_BISECT 128.0
39 #define GRID_NIGHT_ALPHA 0.35
40 #define GRID_DAY_ALPHA 0.55
43 /* RA grid in arcsecs */
44 static const gdouble grid_hms_ra[] = {
45 15.0 * 60.0 * 60.0,
46 15.0 * 60.0 * 10.0,
47 15.0 * 60.0 * 5.0,
48 15.0 * 60.0,
49 15.0 * 10.0,
50 15.0 * 5.0,
51 15.0,
52 7.5,
53 1.5,
56 /* DEC drid in arcsecs */
57 static const gdouble grid_dms_dec[] = {
58 3600.0 * 10.0,
59 3600.0 * 5.0,
60 3600.0,
61 1200.0,
62 600.0,
63 300.0,
64 60.0,
65 10.0,
66 5.0,
67 1.0,
70 static inline int is_tile_visible(struct projection *proj, gint ra, gint dec)
72 struct render_object robject;
73 struct ln_equ_posn pos;
75 pos.ra = ra * 15.0;
76 pos.dec = -90 + dec * 10.0;
77 robject.coord[0].posn = &pos;
78 proj->trans->sky_to_proj_equ(proj, &robject.coord[0]);
79 return projection_is_visible0(proj, &robject);
82 static inline void mark_tiles(struct projection *proj,
83 gint ra, gint dec)
85 gint ra_start = ra - 1, ra_end = ra + 1;
86 gint dec_start = dec - 1, dec_end = dec + 1;
88 if (ra_start < 0)
89 ra_start = GRID_RA_TILES -1;
90 if (ra_end == GRID_RA_TILES)
91 ra_end = 0;
92 if (dec_start < 0)
93 dec_start = 0;
94 if (dec_end == GRID_DEC_TILES)
95 dec_end = GRID_DEC_TILES - 1;
97 proj->grid_tile[ra][dec] = 1;
98 proj->grid_tile[ra_start][dec_start] = 1;
99 proj->grid_tile[ra_end][dec_start] = 1;
100 proj->grid_tile[ra_start][dec_end] = 1;
101 proj->grid_tile[ra_end][dec_end] = 1;
102 proj->grid_tile[ra][dec_start] = 1;
103 proj->grid_tile[ra][dec_end] = 1;
104 proj->grid_tile[ra_start][dec] = 1;
105 proj->grid_tile[ra_end][dec] = 1;
108 static void get_visible_tiles(struct projection *proj)
110 gint ra, dec;
112 /* clear all tiles first */
113 for (ra = 0; ra < GRID_RA_TILES; ra++) {
114 for (dec = 0; dec < GRID_DEC_TILES; dec++) {
115 proj->grid_tile[ra][dec] = 0;
119 /* check each tile */
120 for (ra = 0; ra < GRID_RA_TILES; ra++) {
121 for (dec = 0; dec < GRID_DEC_TILES; dec++) {
122 if (is_tile_visible(proj, ra, dec))
123 mark_tiles(proj, ra, dec);
127 /* now check centre RA, DEC if tile size > fov */
128 if (proj->fov <= 17.0)
129 mark_tiles(proj, (gint)(proj->centre_1ra / 15.0),
130 (gint)(proj->centre_1dec / 10.0 + 9));
133 /* get grid RA step size for projection in arcsecs */
134 static gdouble get_ra_step_delta(struct projection *proj)
136 gint i;
138 for (i = 0; i < nsize(grid_hms_ra); i++) {
139 if (proj->fov / 1.1 > grid_hms_ra[i] / 3600.0)
140 return grid_hms_ra[i] / 3600.0;
142 return grid_hms_ra[i] / 3600.0;
145 /* get grid DEC step size for projection in arcsecs */
146 static gdouble get_dec_step_delta(struct projection *proj)
148 gint i;
150 for (i = 0; i < nsize(grid_dms_dec); i++) {
151 if (proj->fov / 1.1 > grid_dms_dec[i] / 3600.0)
152 return grid_dms_dec[i] / 3600.0;
154 return grid_dms_dec[i] / 3600.0;
157 static void render_dec_labels_at(struct projection *proj,
158 struct render_object *robject, gdouble dec)
160 gchar text[32];
161 gdouble step_delta, delta_div = 2.0;
162 struct ln_equ_posn pos_start, pos_end;
163 struct ln_dms dms;
165 step_delta = get_dec_step_delta(proj);
167 pos_start.ra = proj->centre_ra;
168 pos_end.ra = proj->centre_ra;
169 pos_start.dec = pos_end.dec = dec;
170 robject->coord[0].posn = &pos_start;
171 robject->coord[1].posn = &pos_end;
173 /* find start position */
174 do {
175 pos_start.ra -= step_delta;
176 proj->trans->sky_to_proj_equ(proj, &robject->coord[0]);
177 proj->trans->sky_to_proj_equ(proj, &robject->coord[1]);
178 } while (projection_is_visible0(proj, robject) &&
179 proj->centre_ra - pos_start.ra < 90.0);
180 pos_start.ra += step_delta;
182 /* binary chop start */
183 do {
184 pos_start.ra -= step_delta / delta_div;
185 proj->trans->sky_to_proj_equ(proj, &robject->coord[0]);
186 proj->trans->sky_to_proj_equ(proj, &robject->coord[1]);
187 if (!projection_is_visible0(proj, robject))
188 pos_start.ra += step_delta / delta_div;
189 delta_div *= 2.0;
190 } while (delta_div < GRID_BISECT);
192 /* find end position */
193 do {
194 pos_end.ra += step_delta;
195 proj->trans->sky_to_proj_equ(proj, &robject->coord[0]);
196 proj->trans->sky_to_proj_equ(proj, &robject->coord[1]);
197 } while (projection_is_visible1(proj, robject) &&
198 pos_end.ra - proj->centre_ra < 90.0);
199 pos_end.ra -= step_delta;
201 /* binary chop end */
202 delta_div = 2.0;
203 do {
204 pos_end.ra += step_delta / delta_div;
205 proj->trans->sky_to_proj_equ(proj, &robject->coord[0]);
206 proj->trans->sky_to_proj_equ(proj, &robject->coord[1]);
207 if (!projection_is_visible1(proj, robject))
208 pos_end.ra -= step_delta / delta_div;
209 delta_div *= 2.0;
210 } while (delta_div < GRID_BISECT);
212 ln_deg_to_dms(dec, &dms);
213 if (dms.neg)
214 text[0] = '-';
215 else
216 text[0] = '+';
217 if (dms.minutes == 0 && dms.seconds == 0.0)
218 sprintf(&text[1], "%2.2dº", dms.degrees);
219 else if (dms.seconds < 0.1)
220 sprintf(&text[1], "%2.2dº%2.2dm", dms.degrees, dms.minutes);
221 else
222 sprintf(&text[1], "%2.2dº%2.2dm%2.0f", dms.degrees, dms.minutes,
223 dms.seconds);
225 cairo_move_to(robject->cr,
226 robject->coord[0].x - 30.0,
227 robject->coord[0].y - 1.0);
228 cairo_show_text(robject->cr, text);
229 cairo_move_to(robject->cr,
230 robject->coord[1].x + 1.0,
231 robject->coord[1].y - 1.0);
232 cairo_show_text(robject->cr, text);
235 static void render_ra_labels_at(struct projection *proj,
236 struct render_object *robject, gdouble ra)
238 gchar text[32];
239 gdouble step_delta, delta_div = 2.0;
240 struct ln_equ_posn pos_start, pos_end;
241 struct ln_hms hms;
243 step_delta = get_ra_step_delta(proj);
245 pos_start.dec = proj->centre_dec;
246 pos_end.dec = proj->centre_dec;
247 pos_start.ra = pos_end.ra = ra;
248 robject->coord[0].posn = &pos_start;
249 robject->coord[1].posn = &pos_end;
251 /* find start position */
252 do {
253 pos_start.dec -= step_delta;
254 proj->trans->sky_to_proj_equ(proj, &robject->coord[0]);
255 proj->trans->sky_to_proj_equ(proj, &robject->coord[1]);
256 } while (projection_is_visible0(proj, robject) &&
257 proj->centre_dec - pos_start.dec < 90.0);
258 pos_start.dec += step_delta;
260 /* binary chop start */
261 do {
262 pos_start.dec -= step_delta / delta_div;
263 proj->trans->sky_to_proj_equ(proj, &robject->coord[0]);
264 proj->trans->sky_to_proj_equ(proj, &robject->coord[1]);
265 if (!projection_is_visible0(proj, robject))
266 pos_start.dec += step_delta / delta_div;
267 delta_div *= 2.0;
268 } while (delta_div < GRID_BISECT);
270 /* find end position */
271 do {
272 pos_end.dec += step_delta;
273 proj->trans->sky_to_proj_equ(proj, &robject->coord[0]);
274 proj->trans->sky_to_proj_equ(proj, &robject->coord[1]);
275 } while (projection_is_visible1(proj, robject) &&
276 pos_end.dec - proj->centre_dec < 90.0);
277 pos_end.dec -= step_delta;
279 /* binary chop end */
280 delta_div = 2.0;
281 do {
282 pos_end.dec += step_delta / delta_div;
283 proj->trans->sky_to_proj_equ(proj, &robject->coord[0]);
284 proj->trans->sky_to_proj_equ(proj, &robject->coord[1]);
285 if (!projection_is_visible1(proj, robject))
286 pos_end.dec -= step_delta / delta_div;
287 delta_div *= 2.0;
288 } while (delta_div < GRID_BISECT);
290 ln_deg_to_hms(ra, &hms);
291 if (hms.minutes == 0 && hms.seconds == 0.0)
292 sprintf(text, "%2.0dh", hms.hours);
293 else if (hms.seconds < 0.1)
294 sprintf(text, "%2.0dh%2.2dm", hms.hours, hms.minutes);
295 else
296 sprintf(text, "%2.0dh%2.2dm%2.0f", hms.hours, hms.minutes,
297 hms.seconds);
299 cairo_move_to(robject->cr,
300 robject->coord[0].x,
301 robject->coord[0].y);
302 cairo_show_text(robject->cr, text);
303 cairo_move_to(robject->cr,
304 robject->coord[1].x,
305 robject->coord[1].y + 15.0); // font size
306 cairo_show_text(robject->cr, text);
310 void equ_render_dec_labels(struct render_object *robject, struct projection *proj)
312 gdouble step, step_delta, dec_start, dec_end;
313 struct ln_equ_posn pos1;
315 pos1.ra = proj->centre_ra;
317 step_delta = get_dec_step_delta(proj);
318 //clip_dec_equ_grid(proj, step_delta, &dec_start, &dec_end);
319 robject->coord[0].posn = &pos1;
321 for (step = dec_start; step <= dec_end; step += step_delta) {
322 pos1.dec = step;
323 proj->trans->sky_to_proj_equ(proj, &robject->coord[0]);
324 render_dec_labels_at(proj, robject, step);
328 void equ_render_ra_labels(struct render_object *robject, struct projection *proj)
330 gdouble step, step_delta, ra_start, ra_end;
331 struct ln_equ_posn pos1;
333 pos1.ra = proj->centre_dec;
335 step_delta = get_ra_step_delta(proj);
336 //clip_ra_equ_grid(proj, step_delta, &ra_start, &ra_end);
337 robject->coord[0].posn = &pos1;
339 for (step = ra_start; step <= ra_end; step += step_delta) {
340 pos1.ra = step;
341 proj->trans->sky_to_proj_equ(proj, &robject->coord[0]);
342 render_ra_labels_at(proj, robject, step);
346 void hrz_render_dec_labels(struct render_object *robject, struct projection *proj)
348 gdouble step, step_delta, dec_start, dec_end;
349 struct ln_equ_posn pos1;
351 pos1.ra = proj->centre_ra;
353 step_delta = get_dec_step_delta(proj);
354 //clip_dec_equ_grid(proj, step_delta, &dec_start, &dec_end);
355 robject->coord[0].posn = &pos1;
357 for (step = dec_start; step <= dec_end; step += step_delta) {
358 pos1.dec = step;
359 proj->trans->sky_to_proj_hrz(proj, &robject->coord[0]);
360 render_dec_labels_at(proj, robject, step);
364 void hrz_render_ra_labels(struct render_object *robject, struct projection *proj)
366 gdouble step, step_delta, ra_start, ra_end;
367 struct ln_equ_posn pos1;
369 pos1.ra = proj->centre_dec;
371 step_delta = get_ra_step_delta(proj);
372 //clip_ra_equ_grid(proj, step_delta, &ra_start, &ra_end);
373 robject->coord[0].posn = &pos1;
375 for (step = ra_start; step <= ra_end; step += step_delta) {
376 pos1.ra = step;
377 proj->trans->sky_to_proj_hrz(proj, &robject->coord[0]);
378 render_ra_labels_at(proj, robject, step);
382 static inline void ra_line(struct render_object *robject,
383 struct projection *proj, struct ln_equ_posn *pos,
384 struct ln_equ_posn *end)
386 gdouble divs = 5.0;//proj->pixels_per_degree
387 gdouble step = (end->dec - pos->dec) / divs;
389 /* draw to RA end */
390 for (pos->dec = pos->dec + step;
391 pos->dec <= end->dec; pos->dec += step) {
393 proj->trans->sky_to_proj_equ(proj, &robject->coord[0]);
394 cairo_line_to(robject->cr,
395 robject->coord[0].x, robject->coord[0].y);
399 static inline void dec_line(struct render_object *robject,
400 struct projection *proj, struct ln_equ_posn *pos,
401 struct ln_equ_posn *end)
403 gdouble divs = 5.0;//proj->pixels_per_degree
404 gdouble step = (end->ra - pos->ra) / divs;
406 /* draw to DEC end */
407 for (pos->ra = pos->ra + step;
408 pos->ra <= end->ra; pos->ra += step) {
410 proj->trans->sky_to_proj_equ(proj, &robject->coord[0]);
411 cairo_line_to(robject->cr,
412 robject->coord[0].x, robject->coord[0].y);
416 static inline void render_square(struct render_object *robject,
417 struct projection *proj, gint ra, gint dec)
419 gdouble ra_step_delta, dec_step_delta;
420 struct ln_equ_posn pos, start_pos, end_pos;
422 ra_step_delta = get_ra_step_delta(proj);
423 dec_step_delta = get_dec_step_delta(proj);
424 robject->coord[0].posn = &pos;
426 /* start corner */
427 start_pos.ra = pos.ra = ra * 15.0;
428 start_pos.dec = pos.dec = -90.0 + dec * 10.0;
430 /* end corner */
431 end_pos.ra = start_pos.ra + 15.0;
432 end_pos.dec = start_pos.dec + 10.0;
434 /* RA lines */
435 for (; pos.ra <= end_pos.ra; pos.ra += ra_step_delta) {
437 /* move to RA start */
438 pos.dec = start_pos.dec;
439 proj->trans->sky_to_proj_equ(proj, &robject->coord[0]);
440 cairo_move_to(robject->cr,
441 robject->coord[0].x, robject->coord[0].y);
443 ra_line(robject, proj, &pos, &end_pos);
444 //render_ra_labels_at(proj, robject, ra_step_delta);
447 /* DEC lines */
448 for (pos.dec = start_pos.dec; pos.dec <= end_pos.dec;
449 pos.dec += dec_step_delta) {
451 /* move to DEC start */
452 pos.ra = start_pos.ra;
453 proj->trans->sky_to_proj_equ(proj, &robject->coord[0]);
454 cairo_move_to(robject->cr,
455 robject->coord[0].x, robject->coord[0].y);
457 dec_line(robject, proj, &pos, &end_pos);
461 void equ_render_grid(struct render_object *robject, struct projection *proj)
463 gint ra, dec;
465 for (ra = 0; ra < GRID_RA_TILES; ra++) {
466 for (dec = 1; dec < GRID_DEC_TILES - 2; dec++)
467 if (proj->grid_tile[ra][dec])
468 render_square(robject, proj, ra, dec);
472 void grid_horizon_ra(struct render_object *robject, struct projection *proj)
474 gdouble step;
475 struct ln_equ_posn pos1;
476 int start_visible, end_visible;
478 pos1.ra = 0;
479 pos1.dec = 0;
480 robject->coord[0].posn = &pos1;
481 proj->trans->sky_to_proj_equ(proj, &robject->coord[0]);
482 start_visible = projection_is_visible0(proj, robject);
483 cairo_move_to(robject->cr,
484 robject->coord[0].x,
485 robject->coord[0].y);
487 for (step = 10; step <= 360; step += 10) {
488 pos1.ra = step;
489 proj->trans->sky_to_proj_equ(proj, &robject->coord[0]);
490 end_visible = projection_is_visible0(proj, robject);
491 if (start_visible || end_visible)
492 cairo_line_to(robject->cr,
493 robject->coord[0].x,
494 robject->coord[0].y);
495 else
496 cairo_move_to(robject->cr,
497 robject->coord[0].x,
498 robject->coord[0].y);
499 start_visible = end_visible;
503 void grid_horizon_altaz(struct render_object *robject, struct projection *proj)
505 gdouble step;
506 struct ln_equ_posn pos1;
507 int start_visible, end_visible;
509 cairo_save(robject->cr);
510 cairo_set_source_rgba(robject->cr, 1, 0.35, 0.55, 0.7);
512 pos1.ra = 0;
513 pos1.dec = 0;
514 robject->coord[0].posn = &pos1;
516 proj->trans->sky_to_proj_hrz(proj, &robject->coord[0]);
517 start_visible = projection_is_visible0(proj, robject);
518 cairo_move_to(robject->cr,
519 robject->coord[0].x,
520 robject->coord[0].y);
522 for (step = 10; step <= 360; step += 10) {
523 pos1.ra = step;
524 proj->trans->sky_to_proj_hrz(proj, &robject->coord[0]);
525 end_visible = projection_is_visible0(proj, robject);
526 if (start_visible || end_visible)
527 cairo_line_to(robject->cr,
528 robject->coord[0].x,
529 robject->coord[0].y);
530 else
531 cairo_move_to(robject->cr,
532 robject->coord[0].x,
533 robject->coord[0].y);
534 start_visible = end_visible;
536 cairo_stroke(robject->cr);
537 cairo_restore(robject->cr);
540 void grid_horizon_news(struct render_object *robject, struct projection *proj)
542 struct ln_equ_posn pos1;
544 cairo_save(robject->cr);
545 cairo_set_font_size (robject->cr, 20.0);
546 cairo_set_source_rgba(robject->cr, 1, 0.35, 0.55, 0.7);
547 robject->coord[0].posn = &pos1;
548 pos1.dec = 0;
550 pos1.ra = 0;
551 proj->trans->sky_to_proj_hrz(proj, &robject->coord[0]);
552 cairo_move_to(robject->cr,
553 robject->coord[0].x,
554 robject->coord[0].y);
555 cairo_show_text(robject->cr, "S");
557 pos1.ra = 90;
558 proj->trans->sky_to_proj_hrz(proj, &robject->coord[0]);
559 cairo_move_to(robject->cr,
560 robject->coord[0].x,
561 robject->coord[0].y);
562 cairo_show_text(robject->cr, "W");
564 pos1.ra = 180;
565 proj->trans->sky_to_proj_hrz(proj, &robject->coord[0]);
566 cairo_move_to(robject->cr,
567 robject->coord[0].x,
568 robject->coord[0].y);
569 cairo_show_text(robject->cr, "N");
571 pos1.ra = 270;
572 proj->trans->sky_to_proj_hrz(proj, &robject->coord[0]);
573 cairo_move_to(robject->cr,
574 robject->coord[0].x,
575 robject->coord[0].y);
576 cairo_show_text(robject->cr, "E");
578 cairo_restore(robject->cr);
581 void grid_render(Sky *sky)
583 struct render_object *robject = &sky->robject;
584 struct projection *proj = &sky->projection;
585 gint labels = sky->marker_settings.show_grid_labels;
586 gdouble alpha;
588 if (robject->flags.night_mode)
589 alpha = GRID_NIGHT_ALPHA;
590 else
591 alpha = GRID_DAY_ALPHA;
593 cairo_save(robject->cr);
594 cairo_set_source_rgba(robject->cr, 0, 0.35, 0.55, alpha);
595 cairo_set_font_size (robject->cr, 13.0);
597 if (robject->type == RT_FAST) //TODO make step size * 2.0 when fast
598 cairo_set_tolerance (robject->cr, 1.0);
600 get_visible_tiles(proj);
602 switch (proj->grid_coords) {
603 case PC_RA_DEC:
604 equ_render_grid(robject, proj);
605 //equ_render_dec_labels(robject, proj);
606 //equ_render_ra_labels(robject, proj);
607 break;
608 case PC_ALT_AZ:
609 //hrz_render_ra(robject, proj, labels);
610 //hrz_render_dec(robject, proj, labels);
611 hrz_render_dec_labels(robject, proj);
612 hrz_render_ra_labels(robject, proj);
613 break;
615 cairo_stroke(robject->cr);
617 //grid_horizon_ra(robject, proj);
618 grid_horizon_altaz(robject, proj);
620 grid_horizon_news(robject, proj);
622 if (robject->type == RT_FAST)
623 cairo_set_tolerance (robject->cr, 0.1); /* do we need this */
624 cairo_restore(robject->cr);