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