1 /* Copyright (C) 2007-2008 The Android Open Source Project 2 ** 3 ** This software is licensed under the terms of the GNU General Public 4 ** License version 2, as published by the Free Software Foundation, and 5 ** may be copied, distributed, and modified under those terms. 6 ** 7 ** This program is distributed in the hope that it will be useful, 8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of 9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 ** GNU General Public License for more details. 11 */ 12 #include "android/skin/trackball.h" 13 #include "android/skin/image.h" 14 #include "android/utils/system.h" 15 #include <math.h> 16 17 /***********************************************************************/ 18 /***********************************************************************/ 19 /***** *****/ 20 /***** T R A C K B A L L *****/ 21 /***** *****/ 22 /***********************************************************************/ 23 /***********************************************************************/ 24 25 // a 3-d vector 26 typedef double VectorRec[3]; 27 typedef double* Vector; 28 29 /* define FIX16_IS_FLOAT to use floats for computations */ 30 #define FIX16_IS_FLOAT 31 32 #ifdef FIX16_IS_FLOAT 33 typedef float Fix16; 34 #define FIX16_ONE 1.0 35 #define FIX16_FROM_FLOAT(x) (x) 36 #define FIX16_TO_FLOAT(x) (x) 37 38 #else 39 typedef int Fix16; 40 41 #define FIX16_SHIFT 16 42 #define FIX16_ONE (1 << FIX16_SHIFT) 43 #define FIX16_FROM_FLOAT(x) (Fix16)((x) * FIX16_ONE) 44 #define FIX16_TO_FLOAT(x) ((x)/(1.0*FIX16_ONE)) 45 46 #endif 47 48 typedef Fix16 Fix16VectorRec[3]; 49 typedef Fix16* Fix16Vector; 50 51 static Fix16 52 fixedvector_len( Fix16Vector v ) 53 { 54 double x = FIX16_TO_FLOAT(v[0]); 55 double y = FIX16_TO_FLOAT(v[1]); 56 double z = FIX16_TO_FLOAT(v[2]); 57 double len = sqrt( x*x + y*y + z*z ); 58 59 return FIX16_FROM_FLOAT(len); 60 } 61 62 static void 63 fixedvector_from_vector( Fix16Vector f, Vector v ) 64 { 65 f[0] = FIX16_FROM_FLOAT(v[0]); 66 f[1] = FIX16_FROM_FLOAT(v[1]); 67 f[2] = FIX16_FROM_FLOAT(v[2]); 68 } 69 70 71 #ifdef FIX16_IS_FLOAT 72 static double 73 fixedvector_dot( Fix16Vector u, Fix16Vector v ) 74 { 75 return u[0]*v[0] + u[1]*v[1] + u[2]*v[2]; 76 } 77 #else 78 static Fix16 79 fixedvector_dot( Fix16Vector u, Fix16Vector v ) 80 { 81 long long t; 82 83 t = (long long)u[0] * v[0] + (long long)u[1] * v[1] + (long long)u[2] * v[2]; 84 return (Fix16)(t >> FIX16_SHIFT); 85 } 86 #endif 87 88 static int 89 norm( int dx, int dy ) 90 { 91 return (int) sqrt( dx*1.0*dx + dy*1.0*dy ); 92 } 93 94 /*** ROTATOR: used to rotate the reference axis when mouse motion happens 95 ***/ 96 97 typedef struct 98 { 99 VectorRec d; 100 VectorRec n; 101 double angle; 102 103 } RotatorRec, *Rotator; 104 105 106 #define ANGLE_FACTOR (M_PI/200) 107 108 static void 109 rotator_reset( Rotator rot, int dx, int dy ) 110 { 111 double len = sqrt( dx*dx + dy*dy ); 112 double zx, zy; 113 114 if (len < 1e-3 ) { 115 zx = 1.; 116 zy = 0; 117 } else { 118 zx = dx / len; 119 zy = dy / len; 120 } 121 rot->d[0] = zx; 122 rot->d[1] = zy; 123 rot->d[2] = 0.; 124 125 rot->n[0] = -rot->d[1]; 126 rot->n[1] = rot->d[0]; 127 rot->n[2] = 0; 128 129 rot->angle = len * ANGLE_FACTOR; 130 } 131 132 static void 133 rotator_apply( Rotator rot, double* vec ) 134 { 135 double d, n, z, d2, z2, cs, sn; 136 137 /* project on D, N, Z */ 138 d = vec[0]*rot->d[0] + vec[1]*rot->d[1]; 139 n = vec[0]*rot->n[0] + vec[1]*rot->n[1]; 140 z = vec[2]; 141 142 /* rotate on D, Z */ 143 cs = cos( rot->angle ); 144 sn = sin( rot->angle ); 145 146 d2 = cs*d + sn*z; 147 z2 = -sn*d + cs*z; 148 149 /* project on X, Y, Z */ 150 vec[0] = d2*rot->d[0] + n*rot->n[0]; 151 vec[1] = d2*rot->d[1] + n*rot->n[1]; 152 vec[2] = z2; 153 } 154 155 /*** TRACKBALL OBJECT 156 ***/ 157 typedef struct { int x, y, offset, alpha; Fix16VectorRec f; } SphereCoordRec, *SphereCoord; 158 159 typedef struct SkinTrackBall 160 { 161 int diameter; 162 unsigned* pixels; 163 SDL_Surface* surface; 164 VectorRec axes[3]; /* current ball axes */ 165 166 #define DOT_GRID 3 /* number of horizontal and vertical cells per side grid */ 167 #define DOT_CELLS 2 /* number of random dots per cell */ 168 #define DOT_MAX (6*DOT_GRID*DOT_GRID*DOT_CELLS) /* total number of dots */ 169 #define DOT_RANDOM_X 1007 /* horizontal random range in each cell */ 170 #define DOT_RANDOM_Y 1007 /* vertical random range in each cell */ 171 172 #define DOT_THRESHOLD FIX16_FROM_FLOAT(0.17) 173 174 Fix16VectorRec dots[ DOT_MAX ]; 175 176 SphereCoordRec* sphere_map; 177 int sphere_count; 178 179 unsigned ball_color; 180 unsigned dot_color; 181 unsigned ring_color; 182 183 Uint32 ticks_last; /* ticks since last move */ 184 int acc_x; 185 int acc_y; 186 int acc_threshold; 187 double acc_scale; 188 189 /* rotation applied to events send to the system */ 190 SkinRotation rotation; 191 192 } TrackBallRec, *TrackBall; 193 194 195 /* The following constants are used to better mimic a real trackball. 196 * 197 * ACC_THRESHOLD is used to filter small ball movements out. 198 * If the length of the relative mouse motion is smaller than this 199 * constant, then no corresponding ball event will be sent to the 200 * system. 201 * 202 * ACC_SCALE is used to scale the relative mouse motion vector into 203 * the corresponding ball motion vector. 204 */ 205 #define ACC_THRESHOLD 20 206 #define ACC_SCALE 0.2 207 208 static void 209 trackball_init( TrackBall ball, int diameter, int ring, 210 unsigned ball_color, unsigned dot_color, 211 unsigned ring_color ) 212 { 213 int diameter2 = diameter + ring*2; 214 215 memset( ball, 0, sizeof(*ball) ); 216 217 ball->acc_threshold = ACC_THRESHOLD; 218 ball->acc_scale = ACC_SCALE; 219 220 /* init SDL surface */ 221 ball->diameter = diameter2; 222 ball->ball_color = ball_color; 223 ball->dot_color = dot_color; 224 ball->ring_color = ring_color; 225 226 ball->rotation = SKIN_ROTATION_0; 227 228 ball->pixels = (unsigned*)calloc( diameter2*diameter2, sizeof(unsigned) ); 229 ball->surface = sdl_surface_from_argb32( ball->pixels, diameter2, diameter2 ); 230 231 /* init axes */ 232 ball->axes[0][0] = 1.; ball->axes[0][1] = 0.; ball->axes[0][2] = 0.; 233 ball->axes[1][0] = 0.; ball->axes[1][1] = 1.; ball->axes[1][2] = 0.; 234 ball->axes[2][0] = 0.; ball->axes[2][1] = 0.; ball->axes[2][2] = 1.; 235 236 /* init dots */ 237 { 238 int side, nn = 0; 239 240 for (side = 0; side < 6; side++) { 241 VectorRec origin, axis1, axis2; 242 int xx, yy; 243 244 switch (side) { 245 case 0: 246 origin[0] = -1; origin[1] = -1; origin[2] = +1; 247 axis1 [0] = 1; axis1 [1] = 0; axis1 [2] = 0; 248 axis2 [0] = 0; axis2 [1] = 1; axis2 [2] = 0; 249 break; 250 case 1: 251 origin[0] = -1; origin[1] = -1; origin[2] = -1; 252 axis1 [0] = 1; axis1 [1] = 0; axis1 [2] = 0; 253 axis2 [0] = 0; axis2 [1] = 1; axis2 [2] = 0; 254 break; 255 case 2: 256 origin[0] = +1; origin[1] = -1; origin[2] = -1; 257 axis1 [0] = 0; axis1 [1] = 0; axis1 [2] = 1; 258 axis2 [0] = 0; axis2 [1] = 1; axis2 [2] = 0; 259 break; 260 case 3: 261 origin[0] = -1; origin[1] = -1; origin[2] = -1; 262 axis1 [0] = 0; axis1 [1] = 0; axis1 [2] = 1; 263 axis2 [0] = 0; axis2 [1] = 1; axis2 [2] = 0; 264 break; 265 case 4: 266 origin[0] = -1; origin[1] = -1; origin[2] = -1; 267 axis1 [0] = 1; axis1 [1] = 0; axis1 [2] = 0; 268 axis2 [0] = 0; axis2 [1] = 0; axis2 [2] = 1; 269 break; 270 default: 271 origin[0] = -1; origin[1] = +1; origin[2] = -1; 272 axis1 [0] = 1; axis1 [1] = 0; axis1 [2] = 0; 273 axis2 [0] = 0; axis2 [1] = 0; axis2 [2] = 1; 274 } 275 276 for (xx = 0; xx < DOT_GRID; xx++) { 277 double tx = xx*(2./DOT_GRID); 278 for (yy = 0; yy < DOT_GRID; yy++) { 279 double ty = yy*(2./DOT_GRID); 280 double x0 = origin[0] + axis1[0]*tx + axis2[0]*ty; 281 double y0 = origin[1] + axis1[1]*tx + axis2[1]*ty; 282 double z0 = origin[2] + axis1[2]*tx + axis2[2]*ty; 283 int cc; 284 for (cc = 0; cc < DOT_CELLS; cc++) { 285 double h = (rand() % DOT_RANDOM_X)/((double)DOT_RANDOM_X*DOT_GRID/2); 286 double v = (rand() % DOT_RANDOM_Y)/((double)DOT_RANDOM_Y*DOT_GRID/2); 287 double x = x0 + axis1[0]*h + axis2[0]*v; 288 double y = y0 + axis1[1]*h + axis2[1]*v; 289 double z = z0 + axis1[2]*h + axis2[2]*v; 290 double invlen = 1/sqrt( x*x + y*y + z*z ); 291 292 ball->dots[nn][0] = FIX16_FROM_FLOAT(x*invlen); 293 ball->dots[nn][1] = FIX16_FROM_FLOAT(y*invlen); 294 ball->dots[nn][2] = FIX16_FROM_FLOAT(z*invlen); 295 nn++; 296 } 297 } 298 } 299 } 300 } 301 302 /* init sphere */ 303 { 304 int diameter2 = diameter + 2*ring; 305 double radius = diameter*0.5; 306 double radius2 = diameter2*0.5; 307 int xx, yy; 308 int empty = 0, total = 0; 309 310 ball->sphere_map = calloc( diameter2*diameter2, sizeof(SphereCoordRec) ); 311 312 for (yy = 0; yy < diameter2; yy++) { 313 for (xx = 0; xx < diameter2; xx++) { 314 double x0 = xx - radius2; 315 double y0 = yy - radius2; 316 double r0 = sqrt( x0*x0 + y0*y0 ); 317 SphereCoord coord = &ball->sphere_map[total]; 318 319 if (r0 <= radius) { /* ball pixel */ 320 double rx = x0/radius; 321 double ry = y0/radius; 322 double rz = sqrt( 1.0 - rx*rx - ry*ry ); 323 324 coord->x = xx; 325 coord->y = yy; 326 coord->offset = xx + yy*diameter2; 327 coord->alpha = 256; 328 coord->f[0] = FIX16_FROM_FLOAT(rx); 329 coord->f[1] = FIX16_FROM_FLOAT(ry); 330 coord->f[2] = FIX16_FROM_FLOAT(rz); 331 if (r0 >= radius-1.) { 332 coord->alpha = 256*(radius - r0); 333 } 334 /* illumination model */ 335 { 336 #define LIGHT_X -2.0 337 #define LIGHT_Y -2.5 338 #define LIGHT_Z 5.0 339 340 double lx = LIGHT_X - rx; 341 double ly = LIGHT_Y - ry; 342 double lz = LIGHT_Z - rz; 343 double lir = 1/sqrt(lx*lx + ly*ly + lz*lz); 344 double cosphi = lir*(lx*rx + ly*ry + lz*rz); 345 double scale = 1.1*cosphi + 0.3; 346 347 if (scale < 0) 348 scale = 0; 349 350 coord->alpha = coord->alpha * scale; 351 } 352 total++; 353 } else if (r0 <= radius2) { /* ring pixel */ 354 coord->x = xx; 355 coord->y = yy; 356 coord->offset = xx + yy*diameter2; 357 coord->alpha = 0; 358 if (r0 >= radius2-1.) { 359 coord->alpha = -256*(r0 - (radius2-1.)); 360 } 361 total++; 362 363 } else /* outside pixel */ 364 empty++; 365 } 366 } 367 ball->sphere_count = total; 368 } 369 } 370 371 static int 372 trackball_contains( TrackBall ball, int x, int y ) 373 { 374 return ( (unsigned)(x) < (unsigned)ball->diameter && 375 (unsigned)(y) < (unsigned)ball->diameter ); 376 } 377 378 static void 379 trackball_done( TrackBall ball ) 380 { 381 free( ball->sphere_map ); 382 ball->sphere_map = NULL; 383 ball->sphere_count = 0; 384 385 if (ball->surface) { 386 SDL_FreeSurface( ball->surface ); 387 ball->surface = NULL; 388 } 389 390 if (ball->pixels) { 391 free( ball->pixels ); 392 ball->pixels = NULL; 393 } 394 } 395 396 /*** TRACKBALL SPHERE PIXELS 397 ***/ 398 static unsigned 399 color_blend( unsigned from, unsigned to, int alpha ) 400 { 401 unsigned from_ag = (from >> 8) & 0x00ff00ff; 402 unsigned to_ag = (to >> 8) & 0x00ff00ff; 403 unsigned from_rb = from & 0x00ff00ff; 404 unsigned to_rb = to & 0x00ff00ff; 405 unsigned result_ag = (from_ag + (alpha*(to_ag - from_ag) >> 8)) & 0xff00ff; 406 unsigned result_rb = (from_rb + (alpha*(to_rb - from_rb) >> 8)) & 0xff00ff; 407 408 return (result_ag << 8) | result_rb; 409 } 410 411 static int 412 trackball_move( TrackBall ball, int dx, int dy ) 413 { 414 RotatorRec rot[1]; 415 Uint32 now = SDL_GetTicks(); 416 417 ball->acc_x += dx; 418 ball->acc_y += dy; 419 420 if ( norm( ball->acc_x, ball->acc_y ) > ball->acc_threshold ) 421 { 422 int ddx = ball->acc_x * ball->acc_scale; 423 int ddy = ball->acc_y * ball->acc_scale; 424 int ddt; 425 426 ball->acc_x = 0; 427 ball->acc_y = 0; 428 429 switch (ball->rotation) { 430 case SKIN_ROTATION_0: 431 break; 432 433 case SKIN_ROTATION_90: 434 ddt = ddx; 435 ddx = ddy; 436 ddy = -ddt; 437 break; 438 439 case SKIN_ROTATION_180: 440 ddx = -ddx; 441 ddy = -ddy; 442 break; 443 444 case SKIN_ROTATION_270: 445 ddt = ddx; 446 ddx = -ddy; 447 ddy = ddt; 448 break; 449 } 450 451 kbd_mouse_event(ddx, ddy, 1, 0); 452 } 453 454 rotator_reset( rot, dx, dy ); 455 rotator_apply( rot, ball->axes[0] ); 456 rotator_apply( rot, ball->axes[1] ); 457 rotator_apply( rot, ball->axes[2] ); 458 459 if ( ball->ticks_last == 0 ) 460 ball->ticks_last = now; 461 else if ( now > ball->ticks_last + (1000/60) ) { 462 ball->ticks_last = now; 463 return 1; 464 } 465 return 0; 466 } 467 468 #define BACK_COLOR 0x00000000 469 #define LIGHT_COLOR 0xffffffff 470 471 static void 472 trackball_refresh( TrackBall ball ) 473 { 474 int diameter = ball->diameter; 475 unsigned* pixels = ball->pixels; 476 Fix16VectorRec faxes[3]; 477 Fix16 dot_threshold = DOT_THRESHOLD * diameter; 478 int nn; 479 480 SDL_LockSurface( ball->surface ); 481 482 fixedvector_from_vector( (Fix16Vector)&faxes[0], (Vector)&ball->axes[0] ); 483 fixedvector_from_vector( (Fix16Vector)&faxes[1], (Vector)&ball->axes[1] ); 484 fixedvector_from_vector( (Fix16Vector)&faxes[2], (Vector)&ball->axes[2] ); 485 486 for (nn = 0; nn < ball->sphere_count; nn++) { 487 SphereCoord coord = &ball->sphere_map[nn]; 488 unsigned color = BACK_COLOR; 489 490 if (coord->alpha > 0) { 491 /* are we near one of the points ? */ 492 Fix16 ax = fixedvector_dot( (Fix16Vector)&coord->f, (Fix16Vector)&faxes[0] ); 493 Fix16 ay = fixedvector_dot( (Fix16Vector)&coord->f, (Fix16Vector)&faxes[1] ); 494 Fix16 az = fixedvector_dot( (Fix16Vector)&coord->f, (Fix16Vector)&faxes[2] ); 495 496 Fix16 best_dist = FIX16_ONE; 497 int pp; 498 499 color = ball->ball_color; 500 501 for (pp = 0; pp < DOT_MAX; pp++) { 502 Fix16VectorRec d; 503 Fix16 dist; 504 505 d[0] = ball->dots[pp][0] - ax; 506 d[1] = ball->dots[pp][1] - ay; 507 d[2] = ball->dots[pp][2] - az; 508 509 if (d[0] > dot_threshold || d[0] < -dot_threshold || 510 d[1] > dot_threshold || d[1] < -dot_threshold || 511 d[2] > dot_threshold || d[2] < -dot_threshold ) 512 continue; 513 514 dist = fixedvector_len( (Fix16Vector)&d ); 515 516 if (dist < best_dist) 517 best_dist = dist; 518 } 519 if (best_dist < DOT_THRESHOLD) { 520 int a = 256*(DOT_THRESHOLD - best_dist) / DOT_THRESHOLD; 521 color = color_blend( color, ball->dot_color, a ); 522 } 523 524 if (coord->alpha < 256) { 525 int a = coord->alpha; 526 color = color_blend( ball->ring_color, color, a ); 527 } 528 else if (coord->alpha > 256) { 529 int a = (coord->alpha - 256); 530 color = color_blend( color, LIGHT_COLOR, a ); 531 } 532 } 533 else /* coord->alpha <= 0 */ 534 { 535 color = ball->ring_color; 536 537 if (coord->alpha < 0) { 538 int a = -coord->alpha; 539 color = color_blend( color, BACK_COLOR, a ); 540 } 541 } 542 543 pixels[coord->x + diameter*coord->y] = color; 544 } 545 SDL_UnlockSurface( ball->surface ); 546 } 547 548 void 549 trackball_draw( TrackBall ball, int x, int y, SDL_Surface* dst ) 550 { 551 SDL_Rect d; 552 553 d.x = x; 554 d.y = y; 555 d.w = ball->diameter; 556 d.h = ball->diameter; 557 558 SDL_BlitSurface( ball->surface, NULL, dst, &d ); 559 SDL_UpdateRects( dst, 1, &d ); 560 } 561 562 563 SkinTrackBall* 564 skin_trackball_create ( SkinTrackBallParameters* params ) 565 { 566 TrackBall ball; 567 568 ANEW0(ball); 569 trackball_init( ball, 570 params->diameter, 571 params->ring, 572 params->ball_color, 573 params->dot_color, 574 params->ring_color ); 575 return ball; 576 } 577 578 int 579 skin_trackball_contains( SkinTrackBall* ball, int x, int y ) 580 { 581 return trackball_contains(ball, x, y); 582 } 583 584 int 585 skin_trackball_move( SkinTrackBall* ball, int dx, int dy ) 586 { 587 return trackball_move(ball, dx, dy); 588 } 589 590 void 591 skin_trackball_refresh ( SkinTrackBall* ball ) 592 { 593 trackball_refresh(ball); 594 } 595 596 void 597 skin_trackball_draw( SkinTrackBall* ball, int x, int y, SDL_Surface* dst ) 598 { 599 trackball_draw(ball, x, y, dst); 600 } 601 602 void 603 skin_trackball_destroy ( SkinTrackBall* ball ) 604 { 605 if (ball) { 606 trackball_done(ball); 607 AFREE(ball); 608 } 609 } 610 611 void 612 skin_trackball_rect( SkinTrackBall* ball, SDL_Rect* rect ) 613 { 614 rect->x = 0; 615 rect->y = 0; 616 rect->w = ball->diameter; 617 rect->h = ball->diameter; 618 } 619 620 621 void 622 skin_trackball_set_rotation( SkinTrackBall* ball, SkinRotation rotation ) 623 { 624 ball->rotation = rotation & 3; 625 } 626