Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.cooliris.media;
     18 
     19 import android.os.Bundle;
     20 
     21 public final class GridCamera {
     22     public static final float MAX_CAMERA_SPEED = 12.0f;
     23     public static final float EYE_CONVERGENCE_SPEED = 3.0f;
     24     public static final float EYE_X = 0;
     25     public static final float EYE_Y = 0;
     26     public static final float EYE_Z = 8.0f; // Initial z distance.
     27     private static final float DEFAULT_PORTRAIT_ASPECT = 320.0f / 480.0f;
     28     private static final float DEFAULT_LANDSCAPE_ASPECT = 1.0f / DEFAULT_PORTRAIT_ASPECT;
     29 
     30     public float mEyeX;
     31     public float mEyeY;
     32     public float mEyeZ;
     33     public float mLookAtX;
     34     public float mLookAtY;
     35     public float mLookAtZ;
     36     public float mUpX;
     37     public float mUpY;
     38     public float mUpZ;
     39 
     40     // To tilt the wall.
     41     public float mEyeOffsetX;
     42     public float mEyeOffsetY;
     43     private float mEyeEdgeOffsetX;
     44     private float mEyeEdgeOffsetXAnim;
     45     private float mAmountExceeding;
     46 
     47     // Animation speed, 1.0f is normal speed.
     48     public float mConvergenceSpeed;
     49 
     50     // Camera field of view and its relation to the grid item width.
     51     public float mFov;
     52     public float mScale;
     53     public float mOneByScale;
     54     public int mWidth;
     55     public int mHeight;
     56     public int mItemHeight;
     57     public int mItemWidth;
     58     public float mAspectRatio;
     59     public float mDefaultAspectRatio;
     60 
     61     // Camera positional information.
     62     private float mPosX;
     63     private float mPosY;
     64     private float mPosZ;
     65     private float mTargetPosX;
     66     private float mTargetPosY;
     67     private float mTargetPosZ;
     68     private float mEyeOffsetAnimX;
     69     private float mEyeOffsetAnimY;
     70     private float mTargetEyeX;
     71 
     72     // Screen width and height.
     73     private int mWidthBy2;
     74     private int mHeightBy2;
     75     private float mTanFovBy2;
     76     public float mFriction;
     77 
     78     public GridCamera(int width, int height, int itemWidth, int itemHeight) {
     79         reset();
     80         viewportChanged(width, height, itemWidth, itemHeight);
     81         mConvergenceSpeed = 1.0f;
     82         mFriction = 0.0f;
     83     }
     84 
     85     public void onRestoreInstanceState(Bundle savedInstanceState) {
     86         mEyeX = savedInstanceState.getInt(new String("Camera.mEyeX")) + EYE_X;
     87         mTargetPosX = savedInstanceState.getFloat(new String("Camera.mTargetPosX"));
     88         mTargetPosY = savedInstanceState.getFloat(new String("Camera.mTargetPosY"));
     89         mTargetPosZ = savedInstanceState.getFloat(new String("Camera.mTargetPosZ"));
     90         commitMove();
     91     }
     92 
     93     public void onSaveInstanceState(Bundle outState) {
     94         outState.putFloat(new String("Camera.mEyeX"), mEyeX - EYE_X);
     95         outState.putFloat(new String("Camera.mTargetPosX"), mTargetPosX);
     96         outState.putFloat(new String("Camera.mTargetPosY"), mTargetPosY);
     97         outState.putFloat(new String("Camera.mTargetPosZ"), mTargetPosZ);
     98     }
     99 
    100     public void reset() {
    101         mTargetEyeX = 0;
    102         mEyeX = EYE_X;
    103         mEyeY = EYE_Y;
    104         mEyeZ = EYE_Z;
    105         mLookAtX = EYE_X;
    106         mLookAtY = EYE_Y;
    107         mLookAtZ = 0;
    108         mUpX = 0;
    109         mUpY = 1.0f;
    110         mUpZ = 0;
    111         mPosX = 0;
    112         mPosY = 0;
    113         mPosZ = 0;
    114         mTargetPosX = 0;
    115         mTargetPosY = 0;
    116         mTargetPosZ = 0;
    117     }
    118 
    119     public void viewportChanged(int w, int h, float itemWidth, float itemHeight) {
    120         // For pixel precision we need to use this formula.
    121         /* fov = 2tan-1(qFactor/2*defaultZ) where qFactor = height/ItemHeight */
    122         float qFactor = h / (float) itemHeight;
    123         float fov = 2.0f * (float) Math.toDegrees(Math.atan2(qFactor / 2, GridCamera.EYE_Z));
    124         mWidth = w;
    125         mHeight = h;
    126         mWidthBy2 = w >> 1;
    127         mHeightBy2 = h >> 1;
    128         mAspectRatio = (h == 0) ? 1.0f : (float) w / (float) h;
    129         mDefaultAspectRatio = (w > h) ? DEFAULT_LANDSCAPE_ASPECT : DEFAULT_PORTRAIT_ASPECT;
    130         mTanFovBy2 = (float) Math.tan(Math.toRadians(fov * 0.5f));
    131         mItemHeight = (int) itemHeight;
    132         mItemWidth = (int) itemWidth;
    133         mScale = itemHeight;
    134         mOneByScale = 1.0f / (float) itemHeight;
    135         mFov = fov;
    136     }
    137 
    138     public void convertToScreenSpace(int posX, int posY, int posZ, Vector3f retVal) {
    139         // TODO
    140     }
    141 
    142     public void convertToCameraSpace(float posX, float posY, float posZ, Vector3f retVal) {
    143         float posXx = posX - mWidthBy2;
    144         float posYx = posY - mHeightBy2;
    145         convertToRelativeCameraSpace(posXx, posYx, posZ, retVal);
    146         retVal.x += (EYE_X + mTargetPosX);
    147         retVal.y += (mTargetPosY);
    148     }
    149 
    150     public void convertToRelativeCameraSpace(float posX, float posY, float posZ, Vector3f retVal) {
    151         float posXx = posX;
    152         float posYx = posY;
    153         posXx = posXx / mWidth;
    154         posYx = posYx / mHeight;
    155         float posZx = posZ;
    156         float zDiscriminant = (mTanFovBy2 * (mTargetPosZ + EYE_Z + posZx));
    157         zDiscriminant *= 2.0f;
    158         float yRange = zDiscriminant;
    159         float xRange = zDiscriminant * mAspectRatio;
    160         posXx = ((posXx * xRange));
    161         posYx = ((posYx * yRange));
    162         retVal.x = posXx;
    163         retVal.y = posYx;
    164     }
    165 
    166     public float getDistanceToFitRect(float f, float g) {
    167         final float thisAspectRatio = (float) f / (float) g;
    168         float h = g;
    169         if (thisAspectRatio > mAspectRatio) {
    170             // The width will hit the screen.
    171             h = (f * mHeight) / mWidth;
    172         }
    173         // To fit ITEM_HEIGHT pixels perfectly, the targetZ value must be the
    174         // 1.0f for the given fov
    175         // Thus to fit h pixels,
    176         h = h / mItemHeight;
    177         float targetZ = h / mTanFovBy2;
    178         targetZ = targetZ * 0.5f;
    179         return -(EYE_Z - targetZ);
    180     }
    181 
    182     public void moveXTo(float posX) {
    183         mTargetPosX = posX;
    184     }
    185 
    186     public void moveYTo(float posY) {
    187         mTargetPosY = posY;
    188     }
    189 
    190     public void moveZTo(float posZ) {
    191         mTargetPosZ = posZ;
    192     }
    193 
    194     public void moveTo(float posX, float posY, float posZ) {
    195         float delta = posX - mTargetPosX;
    196         float maxDelta = mWidth * 2.0f * mOneByScale;
    197         delta = FloatUtils.clamp(delta, -maxDelta, maxDelta);
    198         mTargetPosX += delta;
    199         mTargetPosY = posY;
    200         mTargetPosZ = posZ;
    201     }
    202 
    203     public void moveBy(float posX, float posY, float posZ) {
    204         moveTo(posX + mTargetPosX, posY + mTargetPosY, posZ + mTargetPosZ);
    205     }
    206 
    207     public void commitMove() {
    208         mPosX = mTargetPosX;
    209         mPosY = mTargetPosY;
    210         mPosZ = mTargetPosZ;
    211     }
    212 
    213     public void commitMoveInX() {
    214         mPosX = mTargetPosX;
    215     }
    216 
    217     public void commitMoveInY() {
    218         mPosY = mTargetPosY;
    219     }
    220 
    221     public void commitMoveInZ() {
    222         mPosZ = mTargetPosZ;
    223     }
    224 
    225     public boolean computeConstraints(boolean applyConstraints, boolean applyOverflowFeedback, Vector3f firstSlotPosition,
    226             Vector3f lastSlotPosition) {
    227         boolean retVal = false;
    228         float minX = (firstSlotPosition.x) * (1.0f / mItemHeight);
    229         float maxX = (lastSlotPosition.x) * (1.0f / mItemHeight);
    230         if (mTargetPosX < minX) {
    231             mAmountExceeding += mTargetPosX - minX;
    232             mTargetPosX = minX;
    233             mPosX = minX;
    234             if (applyConstraints) {
    235                 mTargetPosX = minX;
    236                 mFriction = 0.0f;
    237             }
    238             retVal = true;
    239         }
    240         if (mTargetPosX > maxX) {
    241             mAmountExceeding += mTargetPosX - maxX;
    242             mTargetPosX = maxX;
    243             mPosX = maxX;
    244             if (applyConstraints) {
    245                 mTargetPosX = maxX;
    246                 mFriction = 0.0f;
    247             }
    248             retVal = true;
    249         }
    250         if (!retVal) {
    251             float scrollingFromEdgeX = 0.0f;
    252             if (mAmountExceeding < 0.0f) {
    253                 scrollingFromEdgeX = mTargetPosX - minX;
    254             } else {
    255                 scrollingFromEdgeX = maxX - mTargetPosX;
    256             }
    257             if (scrollingFromEdgeX > 0.1f) {
    258                 mAmountExceeding = 0.0f;
    259             }
    260         }
    261         if (applyConstraints) {
    262             mEyeEdgeOffsetX = 0.0f;
    263             // We look at amount exceeding and calculate target position in the
    264             // reverse direction.
    265             final float maxBounceBack = 0.8f;
    266             if (mAmountExceeding < -maxBounceBack)
    267                 mAmountExceeding = -maxBounceBack;
    268             if (mAmountExceeding > maxBounceBack)
    269                 mAmountExceeding = maxBounceBack;
    270             //mTargetPosX -= mAmountExceeding * 0.8f;
    271             if (mTargetPosX > maxX)
    272                 mTargetPosX = maxX;
    273             if (mTargetPosX < minX)
    274                 mTargetPosX = minX;
    275             mAmountExceeding = 0.0f;
    276         } else {
    277             float amountExceedingToUse = mAmountExceeding;
    278             final float maxThreshold = 0.6f;
    279             if (amountExceedingToUse > maxThreshold)
    280                 amountExceedingToUse = maxThreshold;
    281             if (amountExceedingToUse < -maxThreshold)
    282                 amountExceedingToUse = -maxThreshold;
    283             if (applyOverflowFeedback)
    284                 mEyeEdgeOffsetX = -10.0f * amountExceedingToUse;
    285             else
    286                 mEyeEdgeOffsetX = 0.0f;
    287         }
    288         return retVal;
    289     }
    290 
    291     public void stopMovement() {
    292         mTargetPosX = mPosX;
    293         mTargetPosY = mPosY;
    294         mTargetPosZ = mPosZ;
    295     }
    296 
    297     public void stopMovementInX() {
    298         mTargetPosX = mPosX;
    299     }
    300 
    301     public void stopMovementInY() {
    302         mTargetPosY = mPosY;
    303     }
    304 
    305     public void stopMovementInZ() {
    306         mTargetPosZ = mPosZ;
    307     }
    308 
    309     public boolean isAnimating() {
    310         return (mPosX != mTargetPosX || mPosY != mTargetPosY || mPosZ != mTargetPosZ || mEyeOffsetAnimX != mEyeOffsetX || mEyeEdgeOffsetXAnim != mEyeEdgeOffsetX);
    311     }
    312 
    313     public boolean isZAnimating() {
    314         return mPosZ != mTargetPosZ;
    315     }
    316 
    317     public void update(float timeElapsed) {
    318         float factor = mConvergenceSpeed;
    319         timeElapsed = (timeElapsed * factor);
    320         float oldPosX = mPosX;
    321         mPosX = FloatUtils.animate(mPosX, mTargetPosX, timeElapsed);
    322         float diff = mPosX - oldPosX;
    323         if (diff == 0)
    324             mFriction = 0.0f;
    325         mTargetPosX += (diff * mFriction);
    326         mPosY = FloatUtils.animate(mPosY, mTargetPosY, timeElapsed);
    327         mPosZ = FloatUtils.animate(mPosZ, mTargetPosZ, timeElapsed);
    328         if (mEyeZ != EYE_Z) {
    329             mEyeOffsetX = 0;
    330             mEyeOffsetY = 0;
    331         }
    332         mEyeOffsetAnimX = FloatUtils.animate(mEyeOffsetAnimX, mEyeOffsetX, timeElapsed);
    333         mEyeOffsetAnimY = FloatUtils.animate(mEyeOffsetAnimY, mEyeOffsetY, timeElapsed);
    334         mEyeEdgeOffsetXAnim = FloatUtils.animate(mEyeEdgeOffsetXAnim, mEyeEdgeOffsetX, timeElapsed);
    335         mTargetEyeX = EYE_X + mPosX;
    336         if (mEyeZ == EYE_Z) {
    337             mEyeX = mTargetEyeX;
    338             // Enable the line below for achieving tilt while you scroll the
    339             // wall.
    340             // FloatUtils.animate(eyeX_, targetEyeX_, timeElapsedx -
    341             // (timeElapsedx * 0.35f));
    342         } else {
    343             mEyeX = mTargetEyeX;
    344         }
    345         mEyeX += (mEyeOffsetAnimX + mEyeEdgeOffsetXAnim);
    346         mLookAtX = EYE_X + mPosX;
    347         mEyeY = EYE_Y + mPosY;
    348         mLookAtY = EYE_Y + mPosY;
    349         mEyeZ = EYE_Z + mPosZ;
    350         mLookAtZ = mPosZ;
    351     }
    352 }
    353