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 27typedef double VectorRec[3]; 28typedef double* Vector; 29 30/* define FIX16_IS_FLOAT to use floats for computations */ 31#define FIX16_IS_FLOAT 32 33#ifdef FIX16_IS_FLOAT 34typedef 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 40typedef 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 49typedef Fix16 Fix16VectorRec[3]; 50typedef Fix16* Fix16Vector; 51 52static Fix16 53fixedvector_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 63static void 64fixedvector_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 73static double 74fixedvector_dot( Fix16Vector u, Fix16Vector v ) 75{ 76 return u[0]*v[0] + u[1]*v[1] + u[2]*v[2]; 77} 78#else 79static Fix16 80fixedvector_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 89static int 90norm( 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 98typedef 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 109static void 110rotator_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 133static void 134rotator_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 ***/ 158typedef struct { int x, y, offset, alpha; Fix16VectorRec f; } SphereCoordRec, *SphereCoord; 159 160typedef 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 209static void 210trackball_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 372static int 373trackball_contains( TrackBall ball, int x, int y ) 374{ 375 return ( (unsigned)(x) < (unsigned)ball->diameter && 376 (unsigned)(y) < (unsigned)ball->diameter ); 377} 378 379static void 380trackball_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 ***/ 399static unsigned 400color_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 412static int 413trackball_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 472static void 473trackball_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 549void 550trackball_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 564SkinTrackBall* 565skin_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 579int 580skin_trackball_contains( SkinTrackBall* ball, int x, int y ) 581{ 582 return trackball_contains(ball, x, y); 583} 584 585int 586skin_trackball_move( SkinTrackBall* ball, int dx, int dy ) 587{ 588 return trackball_move(ball, dx, dy); 589} 590 591void 592skin_trackball_refresh ( SkinTrackBall* ball ) 593{ 594 trackball_refresh(ball); 595} 596 597void 598skin_trackball_draw( SkinTrackBall* ball, int x, int y, SDL_Surface* dst ) 599{ 600 trackball_draw(ball, x, y, dst); 601} 602 603void 604skin_trackball_destroy ( SkinTrackBall* ball ) 605{ 606 if (ball) { 607 trackball_done(ball); 608 AFREE(ball); 609 } 610} 611 612void 613skin_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 622void 623skin_trackball_set_rotation( SkinTrackBall* ball, SkinRotation rotation ) 624{ 625 ball->rotation = rotation & 3; 626} 627