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 */
30 #include "astro_object.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 */
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
[] = {
56 /* DEC drid in arcsecs */
57 static const gdouble grid_dms_dec
[] = {
70 /* get grid RA step size for projection in arcsecs */
71 static gdouble
get_ra_step_delta(struct projection
*proj
)
75 for (i
= 0; i
< nsize(grid_hms_ra
); i
++) {
76 if (proj
->fov
/ 2.0 > grid_hms_ra
[i
] / 3600.0)
77 return grid_hms_ra
[i
] / 3600.0;
79 return grid_hms_ra
[i
] / 3600.0;
82 /* get grid DEC step size for projection in arcsecs */
83 static gdouble
get_dec_step_delta(struct projection
*proj
)
87 for (i
= 0; i
< nsize(grid_dms_dec
); i
++) {
88 if (proj
->fov
/ 2.0 > grid_dms_dec
[i
] / 3600.0)
89 return grid_dms_dec
[i
] / 3600.0;
91 return grid_dms_dec
[i
] / 3600.0;
94 /* clip DEC grid to current projection FOV */
95 static void clip_dec_grid(struct projection
*proj
, gdouble step_size
,
96 gdouble
*south
, gdouble
*north
)
98 *south
= proj
->centre_dec
- proj
->fov
/ 2.0;
100 *south
= -90.0 + step_size
;
102 *south
= floor(*south
/ step_size
) * step_size
;
104 *north
= proj
->centre_dec
+ proj
->fov
/ 2.0;
106 *north
= 90.0 - step_size
;
108 *north
= floor(*north
/ step_size
) * step_size
;
111 /* clip RA grid to current projection FOV */
112 static void clip_ra_grid(struct projection
*proj
, gdouble step_size
,
113 gdouble
*start
, gdouble
*end
)
115 struct render_object robject
;
116 struct ln_equ_posn pos
[4];
117 gdouble start_
, end_
;
120 /* get RA at each projection corner */
121 robject
.coord
[0].x
= 0;
122 robject
.coord
[0].y
= 0;
123 robject
.coord
[0].posn
= &pos
[0];
124 proj
->trans
->proj_to_sky(proj
, &robject
);
126 robject
.coord
[0].x
= proj
->sky_width
;
127 robject
.coord
[0].y
= 0;
128 robject
.coord
[0].posn
= &pos
[1];
129 proj
->trans
->proj_to_sky(proj
, &robject
);
131 robject
.coord
[0].x
= 0;
132 robject
.coord
[0].y
= proj
->sky_height
;
133 robject
.coord
[0].posn
= &pos
[2];
134 proj
->trans
->proj_to_sky(proj
, &robject
);
136 robject
.coord
[0].x
= proj
->sky_width
;
137 robject
.coord
[0].y
= proj
->sky_height
;
138 robject
.coord
[0].posn
= &pos
[3];
139 proj
->trans
->proj_to_sky(proj
, &robject
);
141 /* get highest and lowest RA from corners */
144 for (i
= 0; i
< 3; i
++) {
145 if (start_
> pos
[i
].ra
)
147 if (end_
< pos
[i
].ra
)
152 *start
= floor(*start
/ step_size
) * step_size
;
155 *end
= floor(*end
/ step_size
) * step_size
;
158 /* should we render a line of this radius as an arc or a straight line */
159 static inline gint
is_arc_line(struct projection
*proj
, gdouble radius
)
161 /* magic number - must refine */
162 if (radius
> proj
->sky_width
* ARC_MAGIC
||
163 radius
< proj
->sky_width
* -ARC_MAGIC
) {
164 //printf("%s rad %f\n", __func__, radius);
170 /* is this arc visible in projection */
171 static gint
is_arc_visible(struct projection
*proj
, gdouble x
, gdouble y
,
174 gdouble x1
, y1
, x2
, y2
;
178 || radius
== FP_INFINITE
179 || radius
== -FP_INFINITE
) {
183 /* create clip area sky size + radius */
186 x2
= proj
->sky_width
+ radius
;
187 y2
= proj
->sky_height
+ radius
;
189 /* are we inside ? */
190 if (x
>= x1
&& x
<= x2
&& y
>= y1
&& y
<= y2
)
196 static inline void do_ra_line(struct render_object
*robject
,
197 struct projection
*proj
, gdouble x
, gdouble y
, gdouble my
)
199 gdouble x1
, y1
, x2
, y2
;
203 x2
= my
* (proj
->sky_height
- y
) + x
;
204 y2
= proj
->sky_height
;
206 cairo_move_to(robject
->cr
, x1
, y1
);
207 cairo_line_to(robject
->cr
, x2
, y2
);
210 /* draw a straight RA grid line */
211 static inline void draw_ra_line(struct render_object
*robject
,
212 struct projection
*proj
, gdouble step
)
214 struct ln_equ_posn pos1
, pos2
;
217 /* calc based on small RA line */
218 pos1
.dec
= proj
->centre_dec
+ 1.0;
219 pos2
.dec
= proj
->centre_dec
-1.0;
220 robject
->num_coords
= 2;
223 pos2
.ra
= step
+ 180.0;
225 robject
->coord
[0].posn
= &pos1
;
226 robject
->coord
[1].posn
= &pos2
;
228 proj
->trans
->sky_to_proj(proj
, robject
);
230 /* calculate line gradient */
231 my
= (robject
->coord
[1].x
- robject
->coord
[0].x
) /
232 (robject
->coord
[1].y
- robject
->coord
[0].y
);
234 do_ra_line(robject
, proj
,
235 robject
->coord
[0].x
, robject
->coord
[0].y
, my
);
238 static inline void do_dec_line(struct render_object
*robject
,
239 struct projection
*proj
, gdouble x
, gdouble y
, gdouble mx
)
241 gdouble x1
, y1
, x2
, y2
;
245 x2
= proj
->sky_width
;
246 y2
= mx
* (proj
->sky_width
- x
) + y
;
248 cairo_move_to(robject
->cr
, x1
, y1
);
249 cairo_line_to(robject
->cr
, x2
, y2
);
252 /* draw a straight DEC grid line */
253 static inline void draw_dec_line(struct render_object
*robject
,
254 struct projection
*proj
, gdouble step
)
256 struct ln_equ_posn pos1
, pos2
;
259 /* calc based on small DEC line */
260 pos1
.ra
= proj
->centre_ra
+ 1.0;
261 pos2
.ra
= proj
->centre_ra
-1.0;
262 robject
->num_coords
= 2;
267 robject
->coord
[0].posn
= &pos1
;
268 robject
->coord
[1].posn
= &pos2
;
270 proj
->trans
->sky_to_proj(proj
, robject
);
272 /* calculate line gradient */
273 mx
= (robject
->coord
[1].y
- robject
->coord
[0].y
) /
274 (robject
->coord
[1].x
- robject
->coord
[0].x
);
276 do_dec_line(robject
, proj
,
277 robject
->coord
[0].x
, robject
->coord
[0].y
, mx
);
280 /* render all visible RA grid lines */
281 static void grid_render_ra(struct render_object
*robject
,
282 struct projection
*proj
, gint labels
)
284 gdouble centre_x
, centre_y
, radius
, x
, y
, step
,
285 step_delta
, ra_start
, ra_end
;
286 struct ln_equ_posn pos1
, pos2
;
288 pos1
.dec
= -proj
->centre_dec
;
289 pos2
.dec
= -proj
->centre_dec
;
290 robject
->num_coords
= 2;
292 step_delta
= get_ra_step_delta(proj
);
293 clip_ra_grid(proj
, step_delta
, &ra_start
, &ra_end
);
295 for (step
= ra_start
; step
<= ra_end
; step
+= step_delta
) {
298 pos2
.ra
= step
+ 180.0;
300 robject
->coord
[0].posn
= &pos1
;
301 robject
->coord
[1].posn
= &pos2
;
303 proj
->trans
->sky_to_proj(proj
, robject
);
305 centre_x
= robject
->coord
[0].x
+
306 ((robject
->coord
[1].x
- robject
->coord
[0].x
) / 2.0);
307 centre_y
= robject
->coord
[0].y
+
308 ((robject
->coord
[1].y
- robject
->coord
[0].y
) / 2.0);
309 x
= centre_x
- robject
->coord
[0].x
;
310 y
= centre_y
- robject
->coord
[0].y
;
311 radius
= sqrt(x
* x
+ y
* y
);
313 if (!is_arc_visible(proj
, centre_x
, centre_y
, radius
) &&
314 step
!= proj
->centre_ra
)
317 /* need cairo_arc_to() */
319 if (step
== 0.0 || step
== 90.0 ||
320 step
== 180.0 || step
== 270.0) {
321 struct ln_equ_posn pos1
, pos2
;
327 robject
->num_coords
= 2;
329 robject
->coord
[0].posn
= &pos1
;
330 robject
->coord
[1].posn
= &pos2
;
331 proj
->trans
->sky_to_proj(proj
, robject
);
333 cairo_new_sub_path(robject
->cr
);
334 cairo_arc_to(robject
->cr
,
335 robject
->coord
[0].x
, robject
->coord
[0].y
,
336 robject
->coord
[1].x
, robject
->coord
[1].y
,
339 if (is_arc_line(proj
, centre_x
))
340 draw_ra_line(robject
, proj
, step
);
342 cairo_new_sub_path(robject
->cr
);
343 cairo_arc(robject
->cr
, centre_x
, centre_y
,
344 radius
, circle_start
, circle_end
);
348 if (is_arc_line(proj
, centre_x
))
349 draw_ra_line(robject
, proj
, step
);
351 cairo_new_sub_path(robject
->cr
);
352 cairo_arc(robject
->cr
, centre_x
, centre_y
,
353 radius
, 0, 2 * M_PI
);
359 /* render all visible DEC grid lines */
360 static void grid_render_dec(struct render_object
*robject
,
361 struct projection
*proj
, gint labels
)
363 gdouble centre_x
, centre_y
, radius
, x
, y
, step
,
364 step_delta
, dec_start
, dec_end
;
365 struct ln_equ_posn pos1
, pos2
;
367 pos1
.ra
= proj
->centre_ra
;
368 pos2
.ra
= proj
->centre_ra
+ 180.0;
369 robject
->num_coords
= 2;
371 step_delta
= get_dec_step_delta(proj
);
372 clip_dec_grid(proj
, step_delta
, &dec_start
, &dec_end
);
374 for (step
= dec_start
; step
<= dec_end
; step
+= step_delta
) {
379 robject
->coord
[0].posn
= &pos1
;
380 robject
->coord
[1].posn
= &pos2
;
382 proj
->trans
->sky_to_proj(proj
, robject
);
384 centre_x
= robject
->coord
[0].x
+
385 ((robject
->coord
[1].x
- robject
->coord
[0].x
) / 2.0);
386 centre_y
= robject
->coord
[0].y
+
387 ((robject
->coord
[1].y
- robject
->coord
[0].y
) / 2.0);
388 x
= centre_x
- robject
->coord
[0].x
;
389 y
= centre_y
- robject
->coord
[0].y
;
390 radius
= sqrt(x
* x
+ y
* y
);
392 if (!is_arc_visible(proj
, centre_x
, centre_y
, radius
) &&
393 step
!= proj
->centre_dec
)
396 cairo_new_sub_path(robject
->cr
);
398 if (is_arc_line(proj
, centre_x
))
399 draw_dec_line(robject
, proj
, step
);
401 cairo_arc(robject
->cr
, centre_x
, centre_y
,
402 radius
, 0, 2 * M_PI
);
406 static void render_dec_labels_at(struct projection
*proj
,
407 struct render_object
*robject
, gdouble dec
)
410 gdouble step_delta
, delta_div
= 2.0;
411 struct ln_equ_posn pos_start
, pos_end
;
414 step_delta
= get_dec_step_delta(proj
);
416 pos_start
.ra
= proj
->centre_ra
;
417 pos_end
.ra
= proj
->centre_ra
;
418 pos_start
.dec
= pos_end
.dec
= dec
;
419 robject
->num_coords
= 2;
420 robject
->coord
[0].posn
= &pos_start
;
421 robject
->coord
[1].posn
= &pos_end
;
423 /* find start position */
425 pos_start
.ra
-= step_delta
;
426 proj
->trans
->sky_to_proj(proj
, robject
);
427 } while (projection_is_visible(proj
, 0, robject
) &&
428 proj
->centre_ra
- pos_start
.ra
< 90.0);
429 pos_start
.ra
+= step_delta
;
431 /* binary chop start */
433 pos_start
.ra
-= step_delta
/ delta_div
;
434 proj
->trans
->sky_to_proj(proj
, robject
);
435 if (!projection_is_visible(proj
, 0, robject
))
436 pos_start
.ra
+= step_delta
/ delta_div
;
438 } while (delta_div
< 128.0);
440 /* find end position */
442 pos_end
.ra
+= step_delta
;
443 proj
->trans
->sky_to_proj(proj
, robject
);
444 } while (projection_is_visible(proj
, 1, robject
) &&
445 pos_end
.ra
- proj
->centre_ra
< 90.0);
446 pos_end
.ra
-= step_delta
;
448 /* binary chop end */
451 pos_end
.ra
+= step_delta
/ delta_div
;
452 proj
->trans
->sky_to_proj(proj
, robject
);
453 if (!projection_is_visible(proj
, 1, robject
))
454 pos_end
.ra
-= step_delta
/ delta_div
;
456 } while (delta_div
< 128.0);
458 ln_deg_to_dms(dec
, &dms
);
463 if (dms
.minutes
== 0 && dms
.seconds
== 0.0)
464 sprintf(&text
[1], "%2.2dº", dms
.degrees
);
465 else if (dms
.seconds
< 0.1)
466 sprintf(&text
[1], "%2.2dº%2.2dm", dms
.degrees
, dms
.minutes
);
468 sprintf(&text
[1], "%2.2dº%2.2dm%2.0f", dms
.degrees
, dms
.minutes
,
471 cairo_move_to(robject
->cr
,
472 robject
->coord
[0].x
- 30.0,
473 robject
->coord
[0].y
- 1.0);
474 cairo_show_text(robject
->cr
, text
);
475 cairo_move_to(robject
->cr
,
476 robject
->coord
[1].x
+ 1.0,
477 robject
->coord
[1].y
- 1.0);
478 cairo_show_text(robject
->cr
, text
);
481 static void render_ra_labels_at(struct projection
*proj
,
482 struct render_object
*robject
, gdouble ra
)
485 gdouble step_delta
, delta_div
= 2.0;
486 struct ln_equ_posn pos_start
, pos_end
;
489 step_delta
= get_ra_step_delta(proj
);
491 pos_start
.dec
= proj
->centre_dec
;
492 pos_end
.dec
= proj
->centre_dec
;
493 pos_start
.ra
= pos_end
.ra
= ra
;
494 robject
->num_coords
= 2;
495 robject
->coord
[0].posn
= &pos_start
;
496 robject
->coord
[1].posn
= &pos_end
;
498 /* find start position */
500 pos_start
.dec
-= step_delta
;
501 proj
->trans
->sky_to_proj(proj
, robject
);
502 } while (projection_is_visible(proj
, 0, robject
) &&
503 proj
->centre_dec
- pos_start
.dec
< 90.0);
504 pos_start
.dec
+= step_delta
;
506 /* binary chop start */
508 pos_start
.dec
-= step_delta
/ delta_div
;
509 proj
->trans
->sky_to_proj(proj
, robject
);
510 if (!projection_is_visible(proj
, 0, robject
))
511 pos_start
.dec
+= step_delta
/ delta_div
;
513 } while (delta_div
< 128.0);
515 /* find end position */
517 pos_end
.dec
+= step_delta
;
518 proj
->trans
->sky_to_proj(proj
, robject
);
519 } while (projection_is_visible(proj
, 1, robject
) &&
520 pos_end
.dec
- proj
->centre_dec
< 90.0);
521 pos_end
.dec
-= step_delta
;
523 /* binary chop end */
526 pos_end
.dec
+= step_delta
/ delta_div
;
527 proj
->trans
->sky_to_proj(proj
, robject
);
528 if (!projection_is_visible(proj
, 1, robject
))
529 pos_end
.dec
-= step_delta
/ delta_div
;
531 } while (delta_div
< 128.0);
533 ln_deg_to_hms(ra
, &hms
);
534 if (hms
.minutes
== 0 && hms
.seconds
== 0.0)
535 sprintf(text
, "%2.0dh", hms
.hours
);
536 else if (hms
.seconds
< 0.1)
537 sprintf(text
, "%2.0dh%2.2dm", hms
.hours
, hms
.minutes
);
539 sprintf(text
, "%2.0dh%2.2dm%2.0f", hms
.hours
, hms
.minutes
,
542 cairo_move_to(robject
->cr
,
544 robject
->coord
[0].y
);
545 cairo_show_text(robject
->cr
, text
);
546 cairo_move_to(robject
->cr
,
548 robject
->coord
[1].y
+ 15.0); // font size
549 cairo_show_text(robject
->cr
, text
);
553 void grid_render_dec_labels(struct render_object
*robject
, struct projection
*proj
)
555 gdouble step
, step_delta
, dec_start
, dec_end
;
556 struct ln_equ_posn pos1
;
558 pos1
.ra
= proj
->centre_ra
;
559 robject
->num_coords
= 1;
561 step_delta
= get_dec_step_delta(proj
);
562 clip_dec_grid(proj
, step_delta
, &dec_start
, &dec_end
);
563 robject
->coord
[0].posn
= &pos1
;
565 for (step
= dec_start
; step
<= dec_end
; step
+= step_delta
) {
567 proj
->trans
->sky_to_proj(proj
, robject
);
568 render_dec_labels_at(proj
, robject
, step
);
572 void grid_render_ra_labels(struct render_object
*robject
, struct projection
*proj
)
574 gdouble step
, step_delta
, ra_start
, ra_end
;
575 struct ln_equ_posn pos1
;
577 pos1
.ra
= proj
->centre_dec
;
578 robject
->num_coords
= 1;
580 step_delta
= get_ra_step_delta(proj
);
581 clip_ra_grid(proj
, step_delta
, &ra_start
, &ra_end
);
582 robject
->coord
[0].posn
= &pos1
;
584 for (step
= ra_start
; step
<= ra_end
; step
+= step_delta
) {
586 proj
->trans
->sky_to_proj(proj
, robject
);
587 render_ra_labels_at(proj
, robject
, step
);
591 void grid_render(struct render_object
*robject
, struct projection
*proj
,
596 if (robject
->flags
.night_mode
)
597 alpha
= GRID_NIGHT_ALPHA
;
599 alpha
= GRID_DAY_ALPHA
;
601 cairo_save(robject
->cr
);
602 cairo_set_source_rgba(robject
->cr
, 0, 0.35, 0.55, alpha
);
604 if (robject
->type
== RT_FAST
)
605 cairo_set_tolerance (robject
->cr
, 1.0);
607 grid_render_ra(robject
, proj
, labels
);
608 grid_render_dec(robject
, proj
, labels
);
610 cairo_stroke(robject
->cr
);
612 cairo_set_font_size (robject
->cr
, 13.0);
613 grid_render_dec_labels(robject
, proj
);
614 grid_render_ra_labels(robject
, proj
);
616 if (robject
->type
== RT_FAST
)
617 cairo_set_tolerance (robject
->cr
, 0.1); /* do we need this */
618 cairo_restore(robject
->cr
);