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