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