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