diff options
Diffstat (limited to 'android/skin/trackball.c')
-rw-r--r-- | android/skin/trackball.c | 625 |
1 files changed, 625 insertions, 0 deletions
diff --git a/android/skin/trackball.c b/android/skin/trackball.c new file mode 100644 index 0000000..b18923a --- /dev/null +++ b/android/skin/trackball.c @@ -0,0 +1,625 @@ +/* Copyright (C) 2007-2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#include "android/skin/trackball.h" +#include "android/skin/image.h" +#include "android/utils/system.h" +#include <math.h> + +/***********************************************************************/ +/***********************************************************************/ +/***** *****/ +/***** T R A C K B A L L *****/ +/***** *****/ +/***********************************************************************/ +/***********************************************************************/ + +// a 3-d vector +typedef double VectorRec[3]; +typedef double* Vector; + +/* define FIX16_IS_FLOAT to use floats for computations */ +#define FIX16_IS_FLOAT + +#ifdef FIX16_IS_FLOAT +typedef float Fix16; +#define FIX16_ONE 1.0 +#define FIX16_FROM_FLOAT(x) (x) +#define FIX16_TO_FLOAT(x) (x) + +#else +typedef int Fix16; + +#define FIX16_SHIFT 16 +#define FIX16_ONE (1 << FIX16_SHIFT) +#define FIX16_FROM_FLOAT(x) (Fix16)((x) * FIX16_ONE) +#define FIX16_TO_FLOAT(x) ((x)/(1.0*FIX16_ONE)) + +#endif + +typedef Fix16 Fix16VectorRec[3]; +typedef Fix16* Fix16Vector; + +static Fix16 +fixedvector_len( Fix16Vector v ) +{ + double x = FIX16_TO_FLOAT(v[0]); + double y = FIX16_TO_FLOAT(v[1]); + double z = FIX16_TO_FLOAT(v[2]); + double len = sqrt( x*x + y*y + z*z ); + + return FIX16_FROM_FLOAT(len); +} + +static void +fixedvector_from_vector( Fix16Vector f, Vector v ) +{ + f[0] = FIX16_FROM_FLOAT(v[0]); + f[1] = FIX16_FROM_FLOAT(v[1]); + f[2] = FIX16_FROM_FLOAT(v[2]); +} + + +#ifdef FIX16_IS_FLOAT +static double +fixedvector_dot( Fix16Vector u, Fix16Vector v ) +{ + return u[0]*v[0] + u[1]*v[1] + u[2]*v[2]; +} +#else +static Fix16 +fixedvector_dot( Fix16Vector u, Fix16Vector v ) +{ + long long t; + + t = (long long)u[0] * v[0] + (long long)u[1] * v[1] + (long long)u[2] * v[2]; + return (Fix16)(t >> FIX16_SHIFT); +} +#endif + +static int +norm( int dx, int dy ) +{ + return (int) sqrt( dx*1.0*dx + dy*1.0*dy ); +} + +/*** ROTATOR: used to rotate the reference axis when mouse motion happens + ***/ + +typedef struct +{ + VectorRec d; + VectorRec n; + double angle; + +} RotatorRec, *Rotator; + + +#define ANGLE_FACTOR (M_PI/200) + +static void +rotator_reset( Rotator rot, int dx, int dy ) +{ + double len = sqrt( dx*dx + dy*dy ); + double zx, zy; + + if (len < 1e-3 ) { + zx = 1.; + zy = 0; + } else { + zx = dx / len; + zy = dy / len; + } + rot->d[0] = zx; + rot->d[1] = zy; + rot->d[2] = 0.; + + rot->n[0] = -rot->d[1]; + rot->n[1] = rot->d[0]; + rot->n[2] = 0; + + rot->angle = len * ANGLE_FACTOR; +} + +static void +rotator_apply( Rotator rot, double* vec ) +{ + double d, n, z, d2, z2, cs, sn; + + /* project on D, N, Z */ + d = vec[0]*rot->d[0] + vec[1]*rot->d[1]; + n = vec[0]*rot->n[0] + vec[1]*rot->n[1]; + z = vec[2]; + + /* rotate on D, Z */ + cs = cos( rot->angle ); + sn = sin( rot->angle ); + + d2 = cs*d + sn*z; + z2 = -sn*d + cs*z; + + /* project on X, Y, Z */ + vec[0] = d2*rot->d[0] + n*rot->n[0]; + vec[1] = d2*rot->d[1] + n*rot->n[1]; + vec[2] = z2; +} + +/*** TRACKBALL OBJECT + ***/ +typedef struct { int x, y, offset, alpha; Fix16VectorRec f; } SphereCoordRec, *SphereCoord; + +typedef struct SkinTrackBall +{ + int diameter; + unsigned* pixels; + SDL_Surface* surface; + VectorRec axes[3]; /* current ball axes */ + +#define DOT_GRID 3 /* number of horizontal and vertical cells per side grid */ +#define DOT_CELLS 2 /* number of random dots per cell */ +#define DOT_MAX (6*DOT_GRID*DOT_GRID*DOT_CELLS) /* total number of dots */ +#define DOT_RANDOM_X 1007 /* horizontal random range in each cell */ +#define DOT_RANDOM_Y 1007 /* vertical random range in each cell */ + +#define DOT_THRESHOLD FIX16_FROM_FLOAT(0.17) + + Fix16VectorRec dots[ DOT_MAX ]; + + SphereCoordRec* sphere_map; + int sphere_count; + + unsigned ball_color; + unsigned dot_color; + unsigned ring_color; + + Uint32 ticks_last; /* ticks since last move */ + int acc_x; + int acc_y; + int acc_threshold; + double acc_scale; + + /* rotation applied to events send to the system */ + SkinRotation rotation; + +} TrackBallRec, *TrackBall; + + +/* The following constants are used to better mimic a real trackball. + * + * ACC_THRESHOLD is used to filter small ball movements out. + * If the length of the relative mouse motion is smaller than this + * constant, then no corresponding ball event will be sent to the + * system. + * + * ACC_SCALE is used to scale the relative mouse motion vector into + * the corresponding ball motion vector. + */ +#define ACC_THRESHOLD 20 +#define ACC_SCALE 0.2 + +static void +trackball_init( TrackBall ball, int diameter, int ring, + unsigned ball_color, unsigned dot_color, + unsigned ring_color ) +{ + int diameter2 = diameter + ring*2; + + memset( ball, 0, sizeof(*ball) ); + + ball->acc_threshold = ACC_THRESHOLD; + ball->acc_scale = ACC_SCALE; + + /* init SDL surface */ + ball->diameter = diameter2; + ball->ball_color = ball_color; + ball->dot_color = dot_color; + ball->ring_color = ring_color; + + ball->rotation = SKIN_ROTATION_0; + + ball->pixels = (unsigned*)calloc( diameter2*diameter2, sizeof(unsigned) ); + ball->surface = sdl_surface_from_argb32( ball->pixels, diameter2, diameter2 ); + + /* init axes */ + ball->axes[0][0] = 1.; ball->axes[0][1] = 0.; ball->axes[0][2] = 0.; + ball->axes[1][0] = 0.; ball->axes[1][1] = 1.; ball->axes[1][2] = 0.; + ball->axes[2][0] = 0.; ball->axes[2][1] = 0.; ball->axes[2][2] = 1.; + + /* init dots */ + { + int side, nn = 0; + + for (side = 0; side < 6; side++) { + VectorRec origin, axis1, axis2; + int xx, yy; + + switch (side) { + case 0: + origin[0] = -1; origin[1] = -1; origin[2] = +1; + axis1 [0] = 1; axis1 [1] = 0; axis1 [2] = 0; + axis2 [0] = 0; axis2 [1] = 1; axis2 [2] = 0; + break; + case 1: + origin[0] = -1; origin[1] = -1; origin[2] = -1; + axis1 [0] = 1; axis1 [1] = 0; axis1 [2] = 0; + axis2 [0] = 0; axis2 [1] = 1; axis2 [2] = 0; + break; + case 2: + origin[0] = +1; origin[1] = -1; origin[2] = -1; + axis1 [0] = 0; axis1 [1] = 0; axis1 [2] = 1; + axis2 [0] = 0; axis2 [1] = 1; axis2 [2] = 0; + break; + case 3: + origin[0] = -1; origin[1] = -1; origin[2] = -1; + axis1 [0] = 0; axis1 [1] = 0; axis1 [2] = 1; + axis2 [0] = 0; axis2 [1] = 1; axis2 [2] = 0; + break; + case 4: + origin[0] = -1; origin[1] = -1; origin[2] = -1; + axis1 [0] = 1; axis1 [1] = 0; axis1 [2] = 0; + axis2 [0] = 0; axis2 [1] = 0; axis2 [2] = 1; + break; + default: + origin[0] = -1; origin[1] = +1; origin[2] = -1; + axis1 [0] = 1; axis1 [1] = 0; axis1 [2] = 0; + axis2 [0] = 0; axis2 [1] = 0; axis2 [2] = 1; + } + + for (xx = 0; xx < DOT_GRID; xx++) { + double tx = xx*(2./DOT_GRID); + for (yy = 0; yy < DOT_GRID; yy++) { + double ty = yy*(2./DOT_GRID); + double x0 = origin[0] + axis1[0]*tx + axis2[0]*ty; + double y0 = origin[1] + axis1[1]*tx + axis2[1]*ty; + double z0 = origin[2] + axis1[2]*tx + axis2[2]*ty; + int cc; + for (cc = 0; cc < DOT_CELLS; cc++) { + double h = (rand() % DOT_RANDOM_X)/((double)DOT_RANDOM_X*DOT_GRID/2); + double v = (rand() % DOT_RANDOM_Y)/((double)DOT_RANDOM_Y*DOT_GRID/2); + double x = x0 + axis1[0]*h + axis2[0]*v; + double y = y0 + axis1[1]*h + axis2[1]*v; + double z = z0 + axis1[2]*h + axis2[2]*v; + double invlen = 1/sqrt( x*x + y*y + z*z ); + + ball->dots[nn][0] = FIX16_FROM_FLOAT(x*invlen); + ball->dots[nn][1] = FIX16_FROM_FLOAT(y*invlen); + ball->dots[nn][2] = FIX16_FROM_FLOAT(z*invlen); + nn++; + } + } + } + } + } + + /* init sphere */ + { + int diameter2 = diameter + 2*ring; + double radius = diameter*0.5; + double radius2 = diameter2*0.5; + int xx, yy; + int empty = 0, total = 0; + + ball->sphere_map = calloc( diameter2*diameter2, sizeof(SphereCoordRec) ); + + for (yy = 0; yy < diameter2; yy++) { + for (xx = 0; xx < diameter2; xx++) { + double x0 = xx - radius2; + double y0 = yy - radius2; + double r0 = sqrt( x0*x0 + y0*y0 ); + SphereCoord coord = &ball->sphere_map[total]; + + if (r0 <= radius) { /* ball pixel */ + double rx = x0/radius; + double ry = y0/radius; + double rz = sqrt( 1.0 - rx*rx - ry*ry ); + + coord->x = xx; + coord->y = yy; + coord->offset = xx + yy*diameter2; + coord->alpha = 256; + coord->f[0] = FIX16_FROM_FLOAT(rx); + coord->f[1] = FIX16_FROM_FLOAT(ry); + coord->f[2] = FIX16_FROM_FLOAT(rz); + if (r0 >= radius-1.) { + coord->alpha = 256*(radius - r0); + } + /* illumination model */ + { +#define LIGHT_X -2.0 +#define LIGHT_Y -2.5 +#define LIGHT_Z 5.0 + + double lx = LIGHT_X - rx; + double ly = LIGHT_Y - ry; + double lz = LIGHT_Z - rz; + double lir = 1/sqrt(lx*lx + ly*ly + lz*lz); + double cosphi = lir*(lx*rx + ly*ry + lz*rz); + double scale = 1.1*cosphi + 0.3; + + if (scale < 0) + scale = 0; + + coord->alpha = coord->alpha * scale; + } + total++; + } else if (r0 <= radius2) { /* ring pixel */ + coord->x = xx; + coord->y = yy; + coord->offset = xx + yy*diameter2; + coord->alpha = 0; + if (r0 >= radius2-1.) { + coord->alpha = -256*(r0 - (radius2-1.)); + } + total++; + + } else /* outside pixel */ + empty++; + } + } + ball->sphere_count = total; + } +} + +static int +trackball_contains( TrackBall ball, int x, int y ) +{ + return ( (unsigned)(x) < (unsigned)ball->diameter && + (unsigned)(y) < (unsigned)ball->diameter ); +} + +static void +trackball_done( TrackBall ball ) +{ + free( ball->sphere_map ); + ball->sphere_map = NULL; + ball->sphere_count = 0; + + if (ball->surface) { + SDL_FreeSurface( ball->surface ); + ball->surface = NULL; + } + + if (ball->pixels) { + free( ball->pixels ); + ball->pixels = NULL; + } +} + +/*** TRACKBALL SPHERE PIXELS + ***/ +static unsigned +color_blend( unsigned from, unsigned to, int alpha ) +{ + unsigned from_ag = (from >> 8) & 0x00ff00ff; + unsigned to_ag = (to >> 8) & 0x00ff00ff; + unsigned from_rb = from & 0x00ff00ff; + unsigned to_rb = to & 0x00ff00ff; + unsigned result_ag = (from_ag + (alpha*(to_ag - from_ag) >> 8)) & 0xff00ff; + unsigned result_rb = (from_rb + (alpha*(to_rb - from_rb) >> 8)) & 0xff00ff; + + return (result_ag << 8) | result_rb; +} + +static int +trackball_move( TrackBall ball, int dx, int dy ) +{ + RotatorRec rot[1]; + Uint32 now = SDL_GetTicks(); + + ball->acc_x += dx; + ball->acc_y += dy; + + if ( norm( ball->acc_x, ball->acc_y ) > ball->acc_threshold ) + { + int ddx = ball->acc_x * ball->acc_scale; + int ddy = ball->acc_y * ball->acc_scale; + int ddt; + + ball->acc_x = 0; + ball->acc_y = 0; + + switch (ball->rotation) { + case SKIN_ROTATION_0: + break; + + case SKIN_ROTATION_90: + ddt = ddx; + ddx = ddy; + ddy = -ddt; + break; + + case SKIN_ROTATION_180: + ddx = -ddx; + ddy = -ddy; + break; + + case SKIN_ROTATION_270: + ddt = ddx; + ddx = -ddy; + ddy = ddt; + break; + } + + kbd_mouse_event(ddx, ddy, 1, 0); + } + + rotator_reset( rot, dx, dy ); + rotator_apply( rot, ball->axes[0] ); + rotator_apply( rot, ball->axes[1] ); + rotator_apply( rot, ball->axes[2] ); + + if ( ball->ticks_last == 0 ) + ball->ticks_last = now; + else if ( now > ball->ticks_last + (1000/60) ) { + ball->ticks_last = now; + return 1; + } + return 0; +} + +#define BACK_COLOR 0x00000000 +#define LIGHT_COLOR 0xffffffff + +static void +trackball_refresh( TrackBall ball ) +{ + int diameter = ball->diameter; + unsigned* pixels = ball->pixels; + Fix16VectorRec faxes[3]; + Fix16 dot_threshold = DOT_THRESHOLD * diameter; + int nn; + + SDL_LockSurface( ball->surface ); + + fixedvector_from_vector( (Fix16Vector)&faxes[0], (Vector)&ball->axes[0] ); + fixedvector_from_vector( (Fix16Vector)&faxes[1], (Vector)&ball->axes[1] ); + fixedvector_from_vector( (Fix16Vector)&faxes[2], (Vector)&ball->axes[2] ); + + for (nn = 0; nn < ball->sphere_count; nn++) { + SphereCoord coord = &ball->sphere_map[nn]; + unsigned color = BACK_COLOR; + + if (coord->alpha > 0) { + /* are we near one of the points ? */ + Fix16 ax = fixedvector_dot( (Fix16Vector)&coord->f, (Fix16Vector)&faxes[0] ); + Fix16 ay = fixedvector_dot( (Fix16Vector)&coord->f, (Fix16Vector)&faxes[1] ); + Fix16 az = fixedvector_dot( (Fix16Vector)&coord->f, (Fix16Vector)&faxes[2] ); + + Fix16 best_dist = FIX16_ONE; + int pp; + + color = ball->ball_color; + + for (pp = 0; pp < DOT_MAX; pp++) { + Fix16VectorRec d; + Fix16 dist; + + d[0] = ball->dots[pp][0] - ax; + d[1] = ball->dots[pp][1] - ay; + d[2] = ball->dots[pp][2] - az; + + if (d[0] > dot_threshold || d[0] < -dot_threshold || + d[1] > dot_threshold || d[1] < -dot_threshold || + d[2] > dot_threshold || d[2] < -dot_threshold ) + continue; + + dist = fixedvector_len( (Fix16Vector)&d ); + + if (dist < best_dist) + best_dist = dist; + } + if (best_dist < DOT_THRESHOLD) { + int a = 256*(DOT_THRESHOLD - best_dist) / DOT_THRESHOLD; + color = color_blend( color, ball->dot_color, a ); + } + + if (coord->alpha < 256) { + int a = coord->alpha; + color = color_blend( ball->ring_color, color, a ); + } + else if (coord->alpha > 256) { + int a = (coord->alpha - 256); + color = color_blend( color, LIGHT_COLOR, a ); + } + } + else /* coord->alpha <= 0 */ + { + color = ball->ring_color; + + if (coord->alpha < 0) { + int a = -coord->alpha; + color = color_blend( color, BACK_COLOR, a ); + } + } + + pixels[coord->x + diameter*coord->y] = color; + } + SDL_UnlockSurface( ball->surface ); +} + +void +trackball_draw( TrackBall ball, int x, int y, SDL_Surface* dst ) +{ + SDL_Rect d; + + d.x = x; + d.y = y; + d.w = ball->diameter; + d.h = ball->diameter; + + SDL_BlitSurface( ball->surface, NULL, dst, &d ); + SDL_UpdateRects( dst, 1, &d ); +} + + +SkinTrackBall* +skin_trackball_create ( SkinTrackBallParameters* params ) +{ + TrackBall ball; + + ANEW0(ball); + trackball_init( ball, + params->diameter, + params->ring, + params->ball_color, + params->dot_color, + params->ring_color ); + return ball; +} + +int +skin_trackball_contains( SkinTrackBall* ball, int x, int y ) +{ + return trackball_contains(ball, x, y); +} + +int +skin_trackball_move( SkinTrackBall* ball, int dx, int dy ) +{ + return trackball_move(ball, dx, dy); +} + +void +skin_trackball_refresh ( SkinTrackBall* ball ) +{ + trackball_refresh(ball); +} + +void +skin_trackball_draw( SkinTrackBall* ball, int x, int y, SDL_Surface* dst ) +{ + trackball_draw(ball, x, y, dst); +} + +void +skin_trackball_destroy ( SkinTrackBall* ball ) +{ + if (ball) { + trackball_done(ball); + AFREE(ball); + } +} + +void +skin_trackball_rect( SkinTrackBall* ball, SDL_Rect* rect ) +{ + rect->x = 0; + rect->y = 0; + rect->w = ball->diameter; + rect->h = ball->diameter; +} + + +void +skin_trackball_set_rotation( SkinTrackBall* ball, SkinRotation rotation ) +{ + ball->rotation = rotation & 3; +} |