paul@0 | 1 | /* |
paul@0 | 2 | * Ben NanoNote graphical user interface utilities. |
paul@0 | 3 | * |
paul@0 | 4 | * Copyright (C) 2013 Paul Boddie |
paul@0 | 5 | * |
paul@0 | 6 | * This program is free software; you can redistribute it and/or modify |
paul@0 | 7 | * it under the terms of the GNU General Public License as published by |
paul@0 | 8 | * the Free Software Foundation; either version 2 of the License, or |
paul@0 | 9 | * (at your option) any later version. |
paul@0 | 10 | */ |
paul@0 | 11 | |
paul@0 | 12 | #include <stdio.h> |
paul@0 | 13 | #include "gui.h" |
paul@0 | 14 | |
paul@0 | 15 | SDL_Surface *screen; |
paul@0 | 16 | gui_printer printer = {0, 0, 255, 255, 255, 255}; |
paul@0 | 17 | |
paul@0 | 18 | void gui_init() |
paul@0 | 19 | { |
paul@0 | 20 | if (SDL_Init(SDL_INIT_VIDEO) < 0) |
paul@0 | 21 | { |
paul@0 | 22 | fprintf(stderr, "SDL error: %s\n", SDL_GetError()); |
paul@0 | 23 | exit(1); |
paul@0 | 24 | } |
paul@0 | 25 | } |
paul@0 | 26 | |
paul@0 | 27 | void gui_display_init() |
paul@0 | 28 | { |
paul@0 | 29 | screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); |
paul@0 | 30 | SDL_ShowCursor(SDL_DISABLE); |
paul@0 | 31 | } |
paul@0 | 32 | |
paul@0 | 33 | void gui_shutdown(int signum) |
paul@0 | 34 | { |
paul@0 | 35 | SDL_Quit(); |
paul@0 | 36 | text_shutdown(signum); |
paul@0 | 37 | } |
paul@0 | 38 | |
paul@0 | 39 | void gui_shutdown_threaded(int signum) |
paul@0 | 40 | { |
paul@0 | 41 | SDL_Quit(); |
paul@0 | 42 | text_shutdown_threaded(signum); |
paul@0 | 43 | } |
paul@0 | 44 | |
paul@0 | 45 | void gui_quit() |
paul@0 | 46 | { |
paul@0 | 47 | SDL_Quit(); |
paul@0 | 48 | text_quit(); |
paul@0 | 49 | } |
paul@0 | 50 | |
paul@0 | 51 | void gui_clear() |
paul@0 | 52 | { |
paul@0 | 53 | gui_fill(0, 0, 0); |
paul@0 | 54 | printer.x = 0; |
paul@0 | 55 | printer.y = 0; |
paul@0 | 56 | } |
paul@0 | 57 | |
paul@0 | 58 | void gui_fill(uint8_t r, uint8_t g, uint8_t b) |
paul@0 | 59 | { |
paul@0 | 60 | SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, r, g, b)); |
paul@0 | 61 | } |
paul@0 | 62 | |
paul@0 | 63 | void gui_next_row(gui_printer *printer, uint16_t rows) |
paul@0 | 64 | { |
paul@0 | 65 | printer->x = 0; |
paul@0 | 66 | printer->y = (printer->y + rows * SCREEN_ROW_HEIGHT) % SCREEN_HEIGHT; |
paul@0 | 67 | } |
paul@0 | 68 | |
paul@0 | 69 | void gui_next_column(gui_printer *printer, uint16_t columns) |
paul@0 | 70 | { |
paul@0 | 71 | printer->x += columns * SCREEN_COLUMN_WIDTH; |
paul@0 | 72 | if (printer->x >= SCREEN_WIDTH) |
paul@0 | 73 | { |
paul@0 | 74 | printer->x = 0; |
paul@0 | 75 | printer->y = (printer->y + SCREEN_ROW_HEIGHT) % SCREEN_HEIGHT; |
paul@0 | 76 | } |
paul@0 | 77 | } |
paul@0 | 78 | |
paul@0 | 79 | int gui_printf(const char *format, ...) |
paul@0 | 80 | { |
paul@0 | 81 | va_list args; |
paul@0 | 82 | static char buffer[GUI_BUFSIZE]; |
paul@0 | 83 | char *this = buffer, *next; |
paul@0 | 84 | int printed = 0; |
paul@0 | 85 | |
paul@0 | 86 | va_start(args, format); |
paul@0 | 87 | vsnprintf(buffer, GUI_BUFSIZE, format, args); |
paul@0 | 88 | va_end(args); |
paul@0 | 89 | |
paul@0 | 90 | do |
paul@0 | 91 | { |
paul@0 | 92 | next = strchr(this, (int) '\n'); |
paul@0 | 93 | if (next != NULL) |
paul@0 | 94 | *next = '\0'; |
paul@0 | 95 | stringRGBA(screen, printer.x, printer.y, this, printer.r, printer.g, printer.b, printer.a); |
paul@0 | 96 | if (next != NULL) |
paul@0 | 97 | gui_next_row(&printer, 1); |
paul@0 | 98 | else |
paul@0 | 99 | gui_next_column(&printer, strlen(this)); |
paul@0 | 100 | printed += strlen(this); |
paul@0 | 101 | this = next; |
paul@0 | 102 | } |
paul@0 | 103 | while (next != NULL); |
paul@0 | 104 | |
paul@0 | 105 | return printed; |
paul@0 | 106 | } |
paul@0 | 107 | |
paul@0 | 108 | /** |
paul@0 | 109 | * Generate sky points. |
paul@0 | 110 | */ |
paul@0 | 111 | void gui_sky(vectorf *viewx, vectorf *viewy, vectorf *viewz) |
paul@0 | 112 | { |
paul@0 | 113 | double direction, elevation; |
paul@0 | 114 | int d, e, estart, efinish, dcount; |
paul@0 | 115 | int16_t lastminx, lastminy, thisminx, thisminy, |
paul@0 | 116 | lastmaxx, lastmaxy, thismaxx, thismaxy; |
paul@0 | 117 | uint32_t colour; |
paul@0 | 118 | vectorf point; |
paul@0 | 119 | |
paul@0 | 120 | direction = vectorf_direction(viewz); |
paul@0 | 121 | elevation = vectorf_elevation(viewz); |
paul@0 | 122 | |
paul@0 | 123 | d = closest(raddeg(direction), SKY_GRID_STEP); |
paul@0 | 124 | |
paul@0 | 125 | /* Plot points between elevations -90 and 90 degrees. */ |
paul@0 | 126 | |
paul@0 | 127 | estart = max(-90, closest(raddeg(elevation), SKY_GRID_STEP) - (SKY_GRID_STEP * 3)); |
paul@0 | 128 | efinish = min(90, closest(raddeg(elevation), SKY_GRID_STEP) + (SKY_GRID_STEP * 3)); |
paul@0 | 129 | |
paul@0 | 130 | for (e = estart; e <= efinish; e += SKY_GRID_STEP) |
paul@0 | 131 | { |
paul@0 | 132 | /* Start from the most central point and work outwards until |
paul@0 | 133 | the points are no longer visible. */ |
paul@0 | 134 | |
paul@0 | 135 | for (dcount = 0; dcount <= 180; dcount += SKY_GRID_STEP) |
paul@0 | 136 | { |
paul@0 | 137 | /* Check point visibility. */ |
paul@0 | 138 | |
paul@0 | 139 | vectorf_polar(degrad(d - dcount), degrad(e), &point); |
paul@0 | 140 | if (vectorf_dot(viewz, &point) <= 0) |
paul@0 | 141 | break; |
paul@0 | 142 | |
paul@0 | 143 | /* Plot points at the given extremes. */ |
paul@0 | 144 | |
paul@0 | 145 | colour = e < 0 ? SKY_LOWER_COLOUR : SKY_UPPER_COLOUR; |
paul@0 | 146 | |
paul@0 | 147 | /* Current minimum extent of the direction. */ |
paul@0 | 148 | |
paul@0 | 149 | thisminx = vectorf_dot(viewx, &point) * PROJECTION_FACTOR + SCREEN_WIDTH / 2; |
paul@0 | 150 | thisminy = vectorf_dot(viewy, &point) * -PROJECTION_FACTOR + SCREEN_HEIGHT / 2; |
paul@0 | 151 | |
paul@0 | 152 | /* Draw from the last point at this elevation. */ |
paul@0 | 153 | |
paul@0 | 154 | if (dcount != 0) |
paul@0 | 155 | { |
paul@0 | 156 | lineColor(screen, |
paul@0 | 157 | lastminx, lastminy, |
paul@0 | 158 | thisminx, thisminy, |
paul@0 | 159 | colour |
paul@0 | 160 | ); |
paul@0 | 161 | } |
paul@0 | 162 | |
paul@0 | 163 | /* Draw to the next elevation. */ |
paul@0 | 164 | |
paul@0 | 165 | gui_sky_vertical(viewx, viewy, viewz, d - dcount, e, thisminx, thisminy, colour); |
paul@0 | 166 | |
paul@0 | 167 | /* Current maximum extent of the direction. */ |
paul@0 | 168 | |
paul@0 | 169 | vectorf_polar(degrad(d + dcount), degrad(e), &point); |
paul@0 | 170 | thismaxx = vectorf_dot(viewx, &point) * PROJECTION_FACTOR + SCREEN_WIDTH / 2; |
paul@0 | 171 | thismaxy = vectorf_dot(viewy, &point) * -PROJECTION_FACTOR + SCREEN_HEIGHT / 2; |
paul@0 | 172 | |
paul@0 | 173 | /* Draw from the last point at this elevation. */ |
paul@0 | 174 | |
paul@0 | 175 | if (dcount != 0) |
paul@0 | 176 | { |
paul@0 | 177 | lineColor(screen, |
paul@0 | 178 | lastmaxx, lastmaxy, |
paul@0 | 179 | thismaxx, thismaxy, |
paul@0 | 180 | colour |
paul@0 | 181 | ); |
paul@0 | 182 | } |
paul@0 | 183 | |
paul@0 | 184 | /* Draw to the next elevation. */ |
paul@0 | 185 | |
paul@0 | 186 | gui_sky_vertical(viewx, viewy, viewz, d + dcount, e, thismaxx, thismaxy, colour); |
paul@0 | 187 | |
paul@0 | 188 | /* Stop after one point at -90 or 90 degree elevations. */ |
paul@0 | 189 | |
paul@0 | 190 | if (abs(e) == 90) |
paul@0 | 191 | { |
paul@0 | 192 | pixelColor(screen, |
paul@0 | 193 | thisminx, thisminy, |
paul@0 | 194 | colour |
paul@0 | 195 | ); |
paul@0 | 196 | break; |
paul@0 | 197 | } |
paul@0 | 198 | |
paul@0 | 199 | lastminx = thisminx; lastminy = thisminy; |
paul@0 | 200 | lastmaxx = thismaxx; lastmaxy = thismaxy; |
paul@0 | 201 | } |
paul@0 | 202 | } |
paul@0 | 203 | } |
paul@0 | 204 | |
paul@0 | 205 | void gui_sky_vertical(vectorf *viewx, vectorf *viewy, vectorf *viewz, int direction, int elevation, int16_t x, int16_t y, uint32_t colour) |
paul@0 | 206 | { |
paul@0 | 207 | vectorf point; |
paul@0 | 208 | |
paul@0 | 209 | if ((elevation >= -(90 - SKY_GRID_STEP)) || (elevation <= (90 - SKY_GRID_STEP))) |
paul@0 | 210 | { |
paul@0 | 211 | vectorf_polar(degrad(direction), degrad(elevation + SKY_GRID_STEP), &point); |
paul@0 | 212 | lineColor(screen, |
paul@0 | 213 | x, y, |
paul@0 | 214 | vectorf_dot(viewx, &point) * PROJECTION_FACTOR + SCREEN_WIDTH / 2, |
paul@0 | 215 | vectorf_dot(viewy, &point) * -PROJECTION_FACTOR + SCREEN_HEIGHT / 2, |
paul@0 | 216 | colour |
paul@0 | 217 | ); |
paul@0 | 218 | |
paul@0 | 219 | if (elevation == -(90 - SKY_GRID_STEP)) |
paul@0 | 220 | { |
paul@0 | 221 | vectorf_polar(degrad(direction), degrad(elevation - SKY_GRID_STEP), &point); |
paul@0 | 222 | lineColor(screen, |
paul@0 | 223 | x, y, |
paul@0 | 224 | vectorf_dot(viewx, &point) * PROJECTION_FACTOR + SCREEN_WIDTH / 2, |
paul@0 | 225 | vectorf_dot(viewy, &point) * -PROJECTION_FACTOR + SCREEN_HEIGHT / 2, |
paul@0 | 226 | colour |
paul@0 | 227 | ); |
paul@0 | 228 | } |
paul@0 | 229 | } |
paul@0 | 230 | } |
paul@0 | 231 | |
paul@0 | 232 | /** |
paul@0 | 233 | * Generate motion vector. |
paul@0 | 234 | */ |
paul@0 | 235 | void gui_motion(vectorf *viewx, vectorf *viewy, vectorf *viewz, vectorf *accelerationD) |
paul@0 | 236 | { |
paul@0 | 237 | vectorf point; |
paul@0 | 238 | uint32_t colour; |
paul@0 | 239 | |
paul@0 | 240 | if (vectorf_dot(viewz, accelerationD) <= 0) |
paul@0 | 241 | { |
paul@0 | 242 | colour = MOTION_REVERSE_COLOUR; |
paul@0 | 243 | vectorf_negate(accelerationD, &point); |
paul@0 | 244 | } |
paul@0 | 245 | else |
paul@0 | 246 | { |
paul@0 | 247 | colour = MOTION_FORWARD_COLOUR; |
paul@0 | 248 | point = *accelerationD; |
paul@0 | 249 | } |
paul@0 | 250 | |
paul@0 | 251 | lineColor(screen, |
paul@0 | 252 | SCREEN_WIDTH / 2, |
paul@0 | 253 | SCREEN_HEIGHT / 2, |
paul@0 | 254 | vectorf_dot(viewx, &point) * PROJECTION_FACTOR + SCREEN_WIDTH / 2, |
paul@0 | 255 | vectorf_dot(viewy, &point) * -PROJECTION_FACTOR + SCREEN_HEIGHT / 2, |
paul@0 | 256 | colour |
paul@0 | 257 | ); |
paul@0 | 258 | } |
paul@0 | 259 | |
paul@0 | 260 | /** |
paul@0 | 261 | * Show the given point in space relative to the origin. |
paul@0 | 262 | */ |
paul@0 | 263 | void gui_point(vectorf *viewx, vectorf *viewy, vectorf *viewz, vectorf *point, uint8_t r, uint8_t g, uint8_t b, uint8_t a) |
paul@0 | 264 | { |
paul@0 | 265 | int16_t x, y, radius; |
paul@0 | 266 | double z; |
paul@0 | 267 | |
paul@0 | 268 | if (fabs(vectorf_dot(viewz, point)) < 1) |
paul@0 | 269 | return; |
paul@0 | 270 | |
paul@0 | 271 | z = vectorf_dot(viewz, point); |
paul@0 | 272 | x = vectorf_dot(viewx, point) * PROJECTION_FACTOR / z + SCREEN_WIDTH / 2; |
paul@0 | 273 | y = vectorf_dot(viewy, point) * -PROJECTION_FACTOR / z + SCREEN_HEIGHT / 2; |
paul@0 | 274 | radius = abs(PROJECTION_FACTOR / z); |
paul@0 | 275 | |
paul@0 | 276 | if (vectorf_dot(viewz, point) < 0) |
paul@0 | 277 | circleRGBA(screen, |
paul@0 | 278 | x, y, radius, |
paul@0 | 279 | r, g, b, a |
paul@0 | 280 | ); |
paul@0 | 281 | else |
paul@0 | 282 | filledCircleRGBA(screen, |
paul@0 | 283 | x, y, radius, |
paul@0 | 284 | r, g, b, a |
paul@0 | 285 | ); |
paul@0 | 286 | } |
paul@0 | 287 | |
paul@0 | 288 | /** |
paul@0 | 289 | * Generate a view from the given point in space of the origin. |
paul@0 | 290 | */ |
paul@0 | 291 | void gui_origin(vectorf *viewx, vectorf *viewy, vectorf *viewz, vectorf *point) |
paul@0 | 292 | { |
paul@0 | 293 | vectorf origin; |
paul@0 | 294 | |
paul@0 | 295 | vectorf_negate(point, &origin); |
paul@0 | 296 | gui_point(viewx, viewy, viewz, &origin, 255, 255, 255, 127); |
paul@0 | 297 | } |
paul@0 | 298 | |
paul@0 | 299 | /** |
paul@0 | 300 | * Draw a simple bar representation of the given vector. |
paul@0 | 301 | */ |
paul@0 | 302 | void gui_bar(vectorf *value) |
paul@0 | 303 | { |
paul@0 | 304 | lineRGBA(screen, |
paul@0 | 305 | SCREEN_WIDTH / 2, |
paul@0 | 306 | SCREEN_HEIGHT / 2, |
paul@0 | 307 | SCREEN_WIDTH / 2 + value->x * SCREEN_WIDTH / 2, |
paul@0 | 308 | SCREEN_HEIGHT / 2 - value->y * SCREEN_HEIGHT / 2, |
paul@0 | 309 | 255, 255, 255, 255); |
paul@0 | 310 | |
paul@0 | 311 | circleRGBA(screen, |
paul@0 | 312 | SCREEN_WIDTH / 2 + value->x * SCREEN_WIDTH / 2, |
paul@0 | 313 | SCREEN_HEIGHT / 2 - value->y * SCREEN_HEIGHT / 2, |
paul@0 | 314 | fabs(value->z) * SCREEN_WIDTH / 2, |
paul@0 | 315 | 255, 255, 255, 255); |
paul@0 | 316 | } |
paul@0 | 317 | |
paul@0 | 318 | void gui_plot(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) |
paul@0 | 319 | { |
paul@0 | 320 | pixelRGBA(screen, x, y, r, g, b, a); |
paul@0 | 321 | } |
paul@0 | 322 | |
paul@0 | 323 | void gui_flush() |
paul@0 | 324 | { |
paul@0 | 325 | SDL_Flip(screen); |
paul@0 | 326 | } |
paul@0 | 327 | |
paul@0 | 328 | imu_ui_op gui_handle_events() |
paul@0 | 329 | { |
paul@0 | 330 | SDL_Event event; |
paul@0 | 331 | |
paul@0 | 332 | while (SDL_PollEvent(&event)) |
paul@0 | 333 | { |
paul@0 | 334 | switch (event.type) |
paul@0 | 335 | { |
paul@0 | 336 | case SDL_KEYDOWN: |
paul@0 | 337 | switch (event.key.keysym.sym) |
paul@0 | 338 | { |
paul@0 | 339 | case SDLK_ESCAPE: |
paul@0 | 340 | case SDLK_q: |
paul@0 | 341 | return IMU_UI_OP_QUIT; |
paul@0 | 342 | |
paul@0 | 343 | case SDLK_c: |
paul@0 | 344 | return IMU_UI_OP_CALIBRATE; |
paul@0 | 345 | |
paul@0 | 346 | case SDLK_r: |
paul@0 | 347 | return IMU_UI_OP_RESET; |
paul@0 | 348 | |
paul@0 | 349 | case SDLK_p: |
paul@0 | 350 | return IMU_UI_OP_PAUSE; |
paul@0 | 351 | |
paul@0 | 352 | default: |
paul@0 | 353 | break; |
paul@0 | 354 | } |
paul@0 | 355 | break; |
paul@0 | 356 | |
paul@0 | 357 | case SDL_QUIT: |
paul@0 | 358 | return IMU_UI_OP_QUIT; |
paul@0 | 359 | |
paul@0 | 360 | default: |
paul@0 | 361 | break; |
paul@0 | 362 | } |
paul@0 | 363 | } |
paul@0 | 364 | |
paul@0 | 365 | return IMU_UI_OP_NULL; |
paul@0 | 366 | } |