• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
fixedvector_len(Fix16Vector v)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
fixedvector_from_vector(Fix16Vector f,Vector v)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
fixedvector_dot(Fix16Vector u,Fix16Vector v)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
fixedvector_dot(Fix16Vector u,Fix16Vector v)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
norm(int dx,int dy)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
rotator_reset(Rotator rot,int dx,int dy)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
rotator_apply(Rotator rot,double * vec)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
trackball_init(TrackBall ball,int diameter,int ring,unsigned ball_color,unsigned dot_color,unsigned ring_color)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
trackball_contains(TrackBall ball,int x,int y)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
trackball_done(TrackBall ball)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
color_blend(unsigned from,unsigned to,int alpha)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
trackball_move(TrackBall ball,int dx,int dy)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
trackball_refresh(TrackBall ball)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
trackball_draw(TrackBall ball,int x,int y,SDL_Surface * dst)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*
skin_trackball_create(SkinTrackBallParameters * params)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
skin_trackball_contains(SkinTrackBall * ball,int x,int y)580 skin_trackball_contains( SkinTrackBall*  ball, int  x, int  y )
581 {
582     return  trackball_contains(ball, x, y);
583 }
584 
585 int
skin_trackball_move(SkinTrackBall * ball,int dx,int dy)586 skin_trackball_move( SkinTrackBall*  ball, int  dx, int  dy )
587 {
588     return  trackball_move(ball, dx, dy);
589 }
590 
591 void
skin_trackball_refresh(SkinTrackBall * ball)592 skin_trackball_refresh ( SkinTrackBall*  ball )
593 {
594     trackball_refresh(ball);
595 }
596 
597 void
skin_trackball_draw(SkinTrackBall * ball,int x,int y,SDL_Surface * dst)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
skin_trackball_destroy(SkinTrackBall * ball)604 skin_trackball_destroy ( SkinTrackBall*  ball )
605 {
606     if (ball) {
607         trackball_done(ball);
608         AFREE(ball);
609     }
610 }
611 
612 void
skin_trackball_rect(SkinTrackBall * ball,SDL_Rect * rect)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
skin_trackball_set_rotation(SkinTrackBall * ball,SkinRotation rotation)623 skin_trackball_set_rotation( SkinTrackBall*  ball, SkinRotation  rotation )
624 {
625     ball->rotation = rotation & 3;
626 }
627