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@2 | 114 | int d, e, eclosest, 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@2 | 119 | char buffer[5]; |
paul@0 | 120 | |
paul@0 | 121 | direction = vectorf_direction(viewz); |
paul@0 | 122 | elevation = vectorf_elevation(viewz); |
paul@0 | 123 | |
paul@0 | 124 | d = closest(raddeg(direction), SKY_GRID_STEP); |
paul@2 | 125 | eclosest = closest(raddeg(elevation), SKY_GRID_STEP); |
paul@0 | 126 | |
paul@0 | 127 | /* Plot points between elevations -90 and 90 degrees. */ |
paul@0 | 128 | |
paul@2 | 129 | estart = max(-90, eclosest - (SKY_GRID_STEP * 3)); |
paul@2 | 130 | efinish = min(90, eclosest + (SKY_GRID_STEP * 3)); |
paul@0 | 131 | |
paul@0 | 132 | for (e = estart; e <= efinish; e += SKY_GRID_STEP) |
paul@0 | 133 | { |
paul@0 | 134 | /* Start from the most central point and work outwards until |
paul@0 | 135 | the points are no longer visible. */ |
paul@0 | 136 | |
paul@0 | 137 | for (dcount = 0; dcount <= 180; dcount += SKY_GRID_STEP) |
paul@0 | 138 | { |
paul@0 | 139 | /* Check point visibility. */ |
paul@0 | 140 | |
paul@0 | 141 | vectorf_polar(degrad(d - dcount), degrad(e), &point); |
paul@0 | 142 | if (vectorf_dot(viewz, &point) <= 0) |
paul@0 | 143 | break; |
paul@0 | 144 | |
paul@0 | 145 | /* Plot points at the given extremes. */ |
paul@0 | 146 | |
paul@0 | 147 | colour = e < 0 ? SKY_LOWER_COLOUR : SKY_UPPER_COLOUR; |
paul@0 | 148 | |
paul@0 | 149 | /* Current minimum extent of the direction. */ |
paul@0 | 150 | |
paul@0 | 151 | thisminx = vectorf_dot(viewx, &point) * PROJECTION_FACTOR + SCREEN_WIDTH / 2; |
paul@0 | 152 | thisminy = vectorf_dot(viewy, &point) * -PROJECTION_FACTOR + SCREEN_HEIGHT / 2; |
paul@0 | 153 | |
paul@0 | 154 | /* Draw from the last point at this elevation. */ |
paul@0 | 155 | |
paul@0 | 156 | if (dcount != 0) |
paul@0 | 157 | { |
paul@0 | 158 | lineColor(screen, |
paul@0 | 159 | lastminx, lastminy, |
paul@0 | 160 | thisminx, thisminy, |
paul@0 | 161 | colour |
paul@0 | 162 | ); |
paul@0 | 163 | } |
paul@0 | 164 | |
paul@0 | 165 | /* Draw to the next elevation. */ |
paul@0 | 166 | |
paul@0 | 167 | gui_sky_vertical(viewx, viewy, viewz, d - dcount, e, thisminx, thisminy, colour); |
paul@0 | 168 | |
paul@0 | 169 | /* Current maximum extent of the direction. */ |
paul@0 | 170 | |
paul@0 | 171 | vectorf_polar(degrad(d + dcount), degrad(e), &point); |
paul@0 | 172 | thismaxx = vectorf_dot(viewx, &point) * PROJECTION_FACTOR + SCREEN_WIDTH / 2; |
paul@0 | 173 | thismaxy = vectorf_dot(viewy, &point) * -PROJECTION_FACTOR + SCREEN_HEIGHT / 2; |
paul@0 | 174 | |
paul@0 | 175 | /* Draw from the last point at this elevation. */ |
paul@0 | 176 | |
paul@0 | 177 | if (dcount != 0) |
paul@0 | 178 | { |
paul@0 | 179 | lineColor(screen, |
paul@0 | 180 | lastmaxx, lastmaxy, |
paul@0 | 181 | thismaxx, thismaxy, |
paul@0 | 182 | colour |
paul@0 | 183 | ); |
paul@0 | 184 | } |
paul@0 | 185 | |
paul@0 | 186 | /* Draw to the next elevation. */ |
paul@0 | 187 | |
paul@0 | 188 | gui_sky_vertical(viewx, viewy, viewz, d + dcount, e, thismaxx, thismaxy, colour); |
paul@0 | 189 | |
paul@0 | 190 | /* Stop after one point at -90 or 90 degree elevations. */ |
paul@0 | 191 | |
paul@0 | 192 | if (abs(e) == 90) |
paul@0 | 193 | { |
paul@0 | 194 | pixelColor(screen, |
paul@0 | 195 | thisminx, thisminy, |
paul@0 | 196 | colour |
paul@0 | 197 | ); |
paul@0 | 198 | break; |
paul@0 | 199 | } |
paul@0 | 200 | |
paul@2 | 201 | /* Write labels for the most central set of points. */ |
paul@2 | 202 | |
paul@2 | 203 | if (e == eclosest) |
paul@2 | 204 | { |
paul@2 | 205 | sprintf(buffer, "%d", d - dcount); |
paul@2 | 206 | stringRGBA(screen, thisminx, thisminy, buffer, 255, 255, 255, 255); |
paul@2 | 207 | sprintf(buffer, "%d", d + dcount); |
paul@2 | 208 | stringRGBA(screen, thismaxx, thismaxy, buffer, 255, 255, 255, 255); |
paul@2 | 209 | } |
paul@2 | 210 | |
paul@0 | 211 | lastminx = thisminx; lastminy = thisminy; |
paul@0 | 212 | lastmaxx = thismaxx; lastmaxy = thismaxy; |
paul@0 | 213 | } |
paul@0 | 214 | } |
paul@0 | 215 | } |
paul@0 | 216 | |
paul@0 | 217 | 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 | 218 | { |
paul@0 | 219 | vectorf point; |
paul@0 | 220 | |
paul@0 | 221 | if ((elevation >= -(90 - SKY_GRID_STEP)) || (elevation <= (90 - SKY_GRID_STEP))) |
paul@0 | 222 | { |
paul@0 | 223 | vectorf_polar(degrad(direction), degrad(elevation + SKY_GRID_STEP), &point); |
paul@0 | 224 | lineColor(screen, |
paul@0 | 225 | x, y, |
paul@0 | 226 | vectorf_dot(viewx, &point) * PROJECTION_FACTOR + SCREEN_WIDTH / 2, |
paul@0 | 227 | vectorf_dot(viewy, &point) * -PROJECTION_FACTOR + SCREEN_HEIGHT / 2, |
paul@0 | 228 | colour |
paul@0 | 229 | ); |
paul@0 | 230 | |
paul@0 | 231 | if (elevation == -(90 - SKY_GRID_STEP)) |
paul@0 | 232 | { |
paul@0 | 233 | vectorf_polar(degrad(direction), degrad(elevation - SKY_GRID_STEP), &point); |
paul@0 | 234 | lineColor(screen, |
paul@0 | 235 | x, y, |
paul@0 | 236 | vectorf_dot(viewx, &point) * PROJECTION_FACTOR + SCREEN_WIDTH / 2, |
paul@0 | 237 | vectorf_dot(viewy, &point) * -PROJECTION_FACTOR + SCREEN_HEIGHT / 2, |
paul@0 | 238 | colour |
paul@0 | 239 | ); |
paul@0 | 240 | } |
paul@0 | 241 | } |
paul@0 | 242 | } |
paul@0 | 243 | |
paul@0 | 244 | /** |
paul@0 | 245 | * Generate motion vector. |
paul@0 | 246 | */ |
paul@0 | 247 | void gui_motion(vectorf *viewx, vectorf *viewy, vectorf *viewz, vectorf *accelerationD) |
paul@0 | 248 | { |
paul@0 | 249 | vectorf point; |
paul@0 | 250 | uint32_t colour; |
paul@0 | 251 | |
paul@0 | 252 | if (vectorf_dot(viewz, accelerationD) <= 0) |
paul@0 | 253 | { |
paul@0 | 254 | colour = MOTION_REVERSE_COLOUR; |
paul@0 | 255 | vectorf_negate(accelerationD, &point); |
paul@0 | 256 | } |
paul@0 | 257 | else |
paul@0 | 258 | { |
paul@0 | 259 | colour = MOTION_FORWARD_COLOUR; |
paul@0 | 260 | point = *accelerationD; |
paul@0 | 261 | } |
paul@0 | 262 | |
paul@0 | 263 | lineColor(screen, |
paul@0 | 264 | SCREEN_WIDTH / 2, |
paul@0 | 265 | SCREEN_HEIGHT / 2, |
paul@0 | 266 | vectorf_dot(viewx, &point) * PROJECTION_FACTOR + SCREEN_WIDTH / 2, |
paul@0 | 267 | vectorf_dot(viewy, &point) * -PROJECTION_FACTOR + SCREEN_HEIGHT / 2, |
paul@0 | 268 | colour |
paul@0 | 269 | ); |
paul@0 | 270 | } |
paul@0 | 271 | |
paul@0 | 272 | /** |
paul@0 | 273 | * Show the given point in space relative to the origin. |
paul@0 | 274 | */ |
paul@0 | 275 | 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 | 276 | { |
paul@0 | 277 | int16_t x, y, radius; |
paul@0 | 278 | double z; |
paul@0 | 279 | |
paul@0 | 280 | if (fabs(vectorf_dot(viewz, point)) < 1) |
paul@0 | 281 | return; |
paul@0 | 282 | |
paul@0 | 283 | z = vectorf_dot(viewz, point); |
paul@0 | 284 | x = vectorf_dot(viewx, point) * PROJECTION_FACTOR / z + SCREEN_WIDTH / 2; |
paul@0 | 285 | y = vectorf_dot(viewy, point) * -PROJECTION_FACTOR / z + SCREEN_HEIGHT / 2; |
paul@0 | 286 | radius = abs(PROJECTION_FACTOR / z); |
paul@0 | 287 | |
paul@0 | 288 | if (vectorf_dot(viewz, point) < 0) |
paul@0 | 289 | circleRGBA(screen, |
paul@0 | 290 | x, y, radius, |
paul@0 | 291 | r, g, b, a |
paul@0 | 292 | ); |
paul@0 | 293 | else |
paul@0 | 294 | filledCircleRGBA(screen, |
paul@0 | 295 | x, y, radius, |
paul@0 | 296 | r, g, b, a |
paul@0 | 297 | ); |
paul@0 | 298 | } |
paul@0 | 299 | |
paul@0 | 300 | /** |
paul@0 | 301 | * Generate a view from the given point in space of the origin. |
paul@0 | 302 | */ |
paul@0 | 303 | void gui_origin(vectorf *viewx, vectorf *viewy, vectorf *viewz, vectorf *point) |
paul@0 | 304 | { |
paul@0 | 305 | vectorf origin; |
paul@0 | 306 | |
paul@0 | 307 | vectorf_negate(point, &origin); |
paul@0 | 308 | gui_point(viewx, viewy, viewz, &origin, 255, 255, 255, 127); |
paul@0 | 309 | } |
paul@0 | 310 | |
paul@0 | 311 | /** |
paul@0 | 312 | * Draw a simple bar representation of the given vector. |
paul@0 | 313 | */ |
paul@0 | 314 | void gui_bar(vectorf *value) |
paul@0 | 315 | { |
paul@0 | 316 | lineRGBA(screen, |
paul@0 | 317 | SCREEN_WIDTH / 2, |
paul@0 | 318 | SCREEN_HEIGHT / 2, |
paul@0 | 319 | SCREEN_WIDTH / 2 + value->x * SCREEN_WIDTH / 2, |
paul@0 | 320 | SCREEN_HEIGHT / 2 - value->y * SCREEN_HEIGHT / 2, |
paul@0 | 321 | 255, 255, 255, 255); |
paul@0 | 322 | |
paul@0 | 323 | circleRGBA(screen, |
paul@0 | 324 | SCREEN_WIDTH / 2 + value->x * SCREEN_WIDTH / 2, |
paul@0 | 325 | SCREEN_HEIGHT / 2 - value->y * SCREEN_HEIGHT / 2, |
paul@0 | 326 | fabs(value->z) * SCREEN_WIDTH / 2, |
paul@0 | 327 | 255, 255, 255, 255); |
paul@0 | 328 | } |
paul@0 | 329 | |
paul@0 | 330 | void gui_plot(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) |
paul@0 | 331 | { |
paul@0 | 332 | pixelRGBA(screen, x, y, r, g, b, a); |
paul@0 | 333 | } |
paul@0 | 334 | |
paul@0 | 335 | void gui_flush() |
paul@0 | 336 | { |
paul@0 | 337 | SDL_Flip(screen); |
paul@0 | 338 | } |
paul@0 | 339 | |
paul@0 | 340 | imu_ui_op gui_handle_events() |
paul@0 | 341 | { |
paul@0 | 342 | SDL_Event event; |
paul@0 | 343 | |
paul@0 | 344 | while (SDL_PollEvent(&event)) |
paul@0 | 345 | { |
paul@0 | 346 | switch (event.type) |
paul@0 | 347 | { |
paul@0 | 348 | case SDL_KEYDOWN: |
paul@0 | 349 | switch (event.key.keysym.sym) |
paul@0 | 350 | { |
paul@0 | 351 | case SDLK_ESCAPE: |
paul@0 | 352 | case SDLK_q: |
paul@0 | 353 | return IMU_UI_OP_QUIT; |
paul@0 | 354 | |
paul@0 | 355 | case SDLK_c: |
paul@0 | 356 | return IMU_UI_OP_CALIBRATE; |
paul@0 | 357 | |
paul@0 | 358 | case SDLK_r: |
paul@0 | 359 | return IMU_UI_OP_RESET; |
paul@0 | 360 | |
paul@0 | 361 | case SDLK_p: |
paul@0 | 362 | return IMU_UI_OP_PAUSE; |
paul@0 | 363 | |
paul@0 | 364 | default: |
paul@0 | 365 | break; |
paul@0 | 366 | } |
paul@0 | 367 | break; |
paul@0 | 368 | |
paul@0 | 369 | case SDL_QUIT: |
paul@0 | 370 | return IMU_UI_OP_QUIT; |
paul@0 | 371 | |
paul@0 | 372 | default: |
paul@0 | 373 | break; |
paul@0 | 374 | } |
paul@0 | 375 | } |
paul@0 | 376 | |
paul@0 | 377 | return IMU_UI_OP_NULL; |
paul@0 | 378 | } |