Bug 716673 - Split out the Axis class from the PanZoomController. r=pcwalton
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 10 Jan 2012 10:06:08 -0500
changeset 84222 c9e0ff7b92eb51cd1b6e130ab804dd320368fa1e
parent 84221 66766a64792184a9ae4466faf131d0c48a236ffa
child 84223 2c6c054efeb20f8736fd3d3e3f8777fca2fc991c
push id21832
push userbmo@edmorley.co.uk
push dateWed, 11 Jan 2012 17:04:15 +0000
treeherdermozilla-central@40c9f9ff9fd5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspcwalton
bugs716673
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 716673 - Split out the Axis class from the PanZoomController. r=pcwalton
mobile/android/base/Makefile.in
mobile/android/base/ui/Axis.java
mobile/android/base/ui/PanZoomController.java
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -116,16 +116,17 @@ FENNEC_JAVA_FILES = \
   gfx/RectUtils.java \
   gfx/ScrollbarLayer.java \
   gfx/SingleTileLayer.java \
   gfx/TextLayer.java \
   gfx/TextureReaper.java \
   gfx/TileLayer.java \
   gfx/ViewportMetrics.java \
   gfx/WidgetTileLayer.java \
+  ui/Axis.java \
   ui/PanZoomController.java \
   ui/SubdocumentScrollHelper.java \
   $(NULL)
 
 FENNEC_PP_JAVA_FILES = \
   App.java \
   LauncherShortcuts.java \
   NotificationHandler.java \
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/ui/Axis.java
@@ -0,0 +1,259 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Patrick Walton <pcwalton@mozilla.com>
+ *   Kartikaya Gupta <kgupta@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.gecko.ui;
+
+import org.mozilla.gecko.FloatUtils;
+
+/**
+ * This class represents the physics for one axis of movement (i.e. either
+ * horizontal or vertical). It tracks the different properties of movement
+ * like displacement, velocity, viewport dimensions, etc. pertaining to
+ * a particular axis.
+ */
+abstract class Axis {
+    // This fraction of velocity remains after every animation frame when the velocity is low.
+    private static final float FRICTION_SLOW = 0.85f;
+    // This fraction of velocity remains after every animation frame when the velocity is high.
+    private static final float FRICTION_FAST = 0.97f;
+    // Below this velocity (in pixels per frame), the friction starts increasing from FRICTION_FAST
+    // to FRICTION_SLOW.
+    private static final float VELOCITY_THRESHOLD = 10.0f;
+    // The maximum velocity change factor between events, per ms, in %.
+    // Direction changes are excluded.
+    private static final float MAX_EVENT_ACCELERATION = 0.012f;
+
+    // The rate of deceleration when the surface has overscrolled.
+    private static final float OVERSCROLL_DECEL_RATE = 0.04f;
+    // The percentage of the surface which can be overscrolled before it must snap back.
+    private static final float SNAP_LIMIT = 0.75f;
+
+    // The minimum amount of space that must be present for an axis to be considered scrollable,
+    // in pixels.
+    private static final float MIN_SCROLLABLE_DISTANCE = 0.5f;
+    // The number of milliseconds per frame assuming 60 fps
+    private static final float MS_PER_FRAME = 1000.0f / 60.0f;
+
+    private enum FlingStates {
+        STOPPED,
+        PANNING,
+        FLINGING,
+    }
+
+    private enum Overscroll {
+        NONE,
+        MINUS,      // Overscrolled in the negative direction
+        PLUS,       // Overscrolled in the positive direction
+        BOTH,       // Overscrolled in both directions (page is zoomed to smaller than screen)
+    }
+
+    private final SubdocumentScrollHelper mSubscroller;
+
+    private float mFirstTouchPos;           /* Position of the first touch event on the current drag. */
+    private float mTouchPos;                /* Position of the most recent touch event on the current drag. */
+    private float mLastTouchPos;            /* Position of the touch event before touchPos. */
+    private float mVelocity;                /* Velocity in this direction; pixels per animation frame. */
+    private boolean mLocked;                /* Whether movement on this axis is locked. */
+    private boolean mDisableSnap;           /* Whether overscroll snapping is disabled. */
+    private float mDisplacement;
+
+    private FlingStates mFlingState;        /* The fling state we're in on this axis. */
+
+    protected abstract float getOrigin();
+    protected abstract float getViewportLength();
+    protected abstract float getPageLength();
+
+    Axis(SubdocumentScrollHelper subscroller) {
+        mSubscroller = subscroller;
+    }
+
+    private float getViewportEnd() {
+        return getOrigin() + getViewportLength();
+    }
+
+    void startTouch(float pos) {
+        mVelocity = 0.0f;
+        mLocked = false;
+        mFirstTouchPos = mTouchPos = mLastTouchPos = pos;
+    }
+
+    float panDistance(float currentPos) {
+        return currentPos - mFirstTouchPos;
+    }
+
+    void setLocked(boolean locked) {
+        mLocked = locked;
+    }
+
+    void saveTouchPos() {
+        mLastTouchPos = mTouchPos;
+    }
+
+    void updateWithTouchAt(float pos, float timeDelta) {
+        float newVelocity = (mTouchPos - pos) / timeDelta * MS_PER_FRAME;
+
+        // If there's a direction change, or current velocity is very low,
+        // allow setting of the velocity outright. Otherwise, use the current
+        // velocity and a maximum change factor to set the new velocity.
+        boolean curVelocityIsLow = Math.abs(mVelocity) < 1.0f;
+        boolean directionChange = (mVelocity > 0) != (newVelocity > 0);
+        if (curVelocityIsLow || (directionChange && !FloatUtils.fuzzyEquals(newVelocity, 0.0f))) {
+            mVelocity = newVelocity;
+        } else {
+            float maxChange = Math.abs(mVelocity * timeDelta * MAX_EVENT_ACCELERATION);
+            mVelocity = Math.min(mVelocity + maxChange, Math.max(mVelocity - maxChange, newVelocity));
+        }
+
+        mTouchPos = pos;
+    }
+
+    boolean overscrolled() {
+        return getOverscroll() != Overscroll.NONE;
+    }
+
+    private Overscroll getOverscroll() {
+        boolean minus = (getOrigin() < 0.0f);
+        boolean plus = (getViewportEnd() > getPageLength());
+        if (minus && plus) {
+            return Overscroll.BOTH;
+        } else if (minus) {
+            return Overscroll.MINUS;
+        } else if (plus) {
+            return Overscroll.PLUS;
+        } else {
+            return Overscroll.NONE;
+        }
+    }
+
+    // Returns the amount that the page has been overscrolled. If the page hasn't been
+    // overscrolled on this axis, returns 0.
+    private float getExcess() {
+        switch (getOverscroll()) {
+        case MINUS:     return -getOrigin();
+        case PLUS:      return getViewportEnd() - getPageLength();
+        case BOTH:      return getViewportEnd() - getPageLength() - getOrigin();
+        default:        return 0.0f;
+        }
+    }
+
+    /*
+     * Returns true if the page is zoomed in to some degree along this axis such that scrolling
+     * is possible. Otherwise, returns false.
+     */
+    private boolean scrollable() {
+        return getViewportLength() <= getPageLength() - MIN_SCROLLABLE_DISTANCE;
+    }
+
+    /*
+     * Returns the resistance, as a multiplier, that should be taken into account when
+     * tracking or pinching.
+     */
+    float getEdgeResistance() {
+        float excess = getExcess();
+        return (excess > 0.0f) ? SNAP_LIMIT - excess / getViewportLength() : 1.0f;
+    }
+
+    /* Returns the velocity. If the axis is locked, returns 0. */
+    float getRealVelocity() {
+        return mLocked ? 0.0f : mVelocity;
+    }
+
+    void startPan() {
+        mFlingState = FlingStates.PANNING;
+    }
+
+    void startFling(boolean stopped) {
+        mDisableSnap = mSubscroller.scrolling();
+
+        if (stopped) {
+            mFlingState = FlingStates.STOPPED;
+        } else {
+            mFlingState = FlingStates.FLINGING;
+        }
+    }
+
+    /* Advances a fling animation by one step. */
+    boolean advanceFling() {
+        if (mFlingState != FlingStates.FLINGING) {
+            return false;
+        }
+
+        float excess = getExcess();
+        if (mDisableSnap || FloatUtils.fuzzyEquals(excess, 0.0f)) {
+            // If we aren't overscrolled, just apply friction.
+            if (Math.abs(mVelocity) >= VELOCITY_THRESHOLD) {
+                mVelocity *= FRICTION_FAST;
+            } else {
+                float t = mVelocity / VELOCITY_THRESHOLD;
+                mVelocity *= FloatUtils.interpolate(FRICTION_SLOW, FRICTION_FAST, t);
+            }
+        } else {
+            // Otherwise, decrease the velocity linearly.
+            float elasticity = 1.0f - excess / (getViewportLength() * SNAP_LIMIT);
+            if (getOverscroll() == Overscroll.MINUS) {
+                mVelocity = Math.min((mVelocity + OVERSCROLL_DECEL_RATE) * elasticity, 0.0f);
+            } else { // must be Overscroll.PLUS
+                mVelocity = Math.max((mVelocity - OVERSCROLL_DECEL_RATE) * elasticity, 0.0f);
+            }
+        }
+
+        return true;
+    }
+
+    void stopFling() {
+        mVelocity = 0.0f;
+        mFlingState = FlingStates.STOPPED;
+    }
+
+    // Performs displacement of the viewport position according to the current velocity.
+    void displace() {
+        if (!mSubscroller.scrolling() && (mLocked || !scrollable()))
+            return;
+
+        if (mFlingState == FlingStates.PANNING)
+            mDisplacement += (mLastTouchPos - mTouchPos) * getEdgeResistance();
+        else
+            mDisplacement += mVelocity;
+    }
+
+    float resetDisplacement() {
+        float d = mDisplacement;
+        mDisplacement = 0.0f;
+        return d;
+    }
+}
--- a/mobile/android/base/ui/PanZoomController.java
+++ b/mobile/android/base/ui/PanZoomController.java
@@ -65,44 +65,25 @@ import java.util.TimerTask;
  *   https://github.com/joehewitt/scrollability/
  */
 public class PanZoomController
     extends GestureDetector.SimpleOnGestureListener
     implements ScaleGestureDetector.OnScaleGestureListener, GeckoEventListener
 {
     private static final String LOGTAG = "GeckoPanZoomController";
 
-    // This fraction of velocity remains after every animation frame when the velocity is low.
-    private static final float FRICTION_SLOW = 0.85f;
-    // This fraction of velocity remains after every animation frame when the velocity is high.
-    private static final float FRICTION_FAST = 0.97f;
-    // Below this velocity (in pixels per frame), the friction starts increasing from FRICTION_FAST
-    // to FRICTION_SLOW.
-    private static final float VELOCITY_THRESHOLD = 10.0f;
     // Animation stops if the velocity is below this value when overscrolled or panning.
     private static final float STOPPED_THRESHOLD = 4.0f;
     // Animation stops is the velocity is below this threshold when flinging.
     private static final float FLING_STOPPED_THRESHOLD = 0.1f;
-    // The percentage of the surface which can be overscrolled before it must snap back.
-    private static final float SNAP_LIMIT = 0.75f;
-    // The rate of deceleration when the surface has overscrolled.
-    private static final float OVERSCROLL_DECEL_RATE = 0.04f;
     // The distance the user has to pan before we recognize it as such (e.g. to avoid
     // 1-pixel pans between the touch-down and touch-up of a click). In units of inches.
     private static final float PAN_THRESHOLD = 0.1f;
     // Angle from axis within which we stay axis-locked
     private static final double AXIS_LOCK_ANGLE = Math.PI / 6.0; // 30 degrees
-    // The maximum velocity change factor between events, per ms, in %.
-    // Direction changes are excluded.
-    private static final float MAX_EVENT_ACCELERATION = 0.012f;
-    // The minimum amount of space that must be present for an axis to be considered scrollable,
-    // in pixels.
-    private static final float MIN_SCROLLABLE_DISTANCE = 0.5f;
-    // The number of milliseconds per frame assuming 60 fps
-    private static final float MS_PER_FRAME = 1000.0f / 60.0f;
     // The maximum amount we allow you to zoom into a page
     private static final float MAX_ZOOM = 4.0f;
 
     /* 16 precomputed frames of the _ease-out_ animation from the CSS Transitions specification. */
     private static final float[] EASE_OUT_ANIMATION_FRAMES = {
         0.00000f,   /* 0 */
         0.10211f,   /* 1 */
         0.19864f,   /* 2 */
@@ -649,203 +630,16 @@ public class PanZoomController
         Log.d(LOGTAG, "Finishing animation at " + mController.getViewportMetrics());
         stopAnimationTimer();
 
         // Force a viewport synchronisation
         mController.setForceRedraw();
         mController.notifyLayerClientOfGeometryChange();
     }
 
-    // Physics information for one axis (X or Y).
-    private abstract static class Axis {
-        private enum FlingStates {
-            STOPPED,
-            PANNING,
-            FLINGING,
-        }
-
-        private enum Overscroll {
-            NONE,
-            MINUS,      // Overscrolled in the negative direction
-            PLUS,       // Overscrolled in the positive direction
-            BOTH,       // Overscrolled in both directions (page is zoomed to smaller than screen)
-        }
-
-        private final SubdocumentScrollHelper mSubscroller;
-
-        private float firstTouchPos;            /* Position of the first touch event on the current drag. */
-        private float touchPos;                 /* Position of the most recent touch event on the current drag. */
-        private float lastTouchPos;             /* Position of the touch event before touchPos. */
-        private float velocity;                  /* Velocity in this direction. */
-        private boolean locked;                  /* Whether movement on this axis is locked. */
-        private boolean disableSnap;             /* Whether overscroll snapping is disabled. */
-
-        private FlingStates mFlingState;        /* The fling state we're in on this axis. */
-
-        public abstract float getOrigin();
-        protected abstract float getViewportLength();
-        protected abstract float getPageLength();
-
-        public float displacement;
-
-        public Axis(SubdocumentScrollHelper subscroller) {
-            mSubscroller = subscroller;
-        }
-
-        private float getViewportEnd() { return getOrigin() + getViewportLength(); }
-
-        public void startTouch(float pos) {
-            velocity = 0.0f;
-            locked = false;
-            firstTouchPos = touchPos = lastTouchPos = pos;
-        }
-
-        float panDistance(float currentPos) {
-            return currentPos - firstTouchPos;
-        }
-
-        void setLocked(boolean locked) {
-            this.locked = locked;
-        }
-
-        void saveTouchPos() {
-            lastTouchPos = touchPos;
-        }
-
-        void updateWithTouchAt(float pos, float timeDelta) {
-            float newVelocity = (touchPos - pos) / timeDelta * MS_PER_FRAME;
-
-            // If there's a direction change, or current velocity is very low,
-            // allow setting of the velocity outright. Otherwise, use the current
-            // velocity and a maximum change factor to set the new velocity.
-            boolean curVelocityIsLow = Math.abs(velocity) < 1.0f;
-            boolean directionChange = (velocity > 0) != (newVelocity > 0);
-            if (curVelocityIsLow || (directionChange && !FloatUtils.fuzzyEquals(newVelocity, 0.0f))) {
-                velocity = newVelocity;
-            } else {
-                float maxChange = Math.abs(velocity * timeDelta * MAX_EVENT_ACCELERATION);
-                velocity = Math.min(velocity + maxChange, Math.max(velocity - maxChange, newVelocity));
-            }
-
-            touchPos = pos;
-        }
-
-        boolean overscrolled() {
-            return getOverscroll() != Overscroll.NONE;
-        }
-
-        private Overscroll getOverscroll() {
-            boolean minus = (getOrigin() < 0.0f);
-            boolean plus = (getViewportEnd() > getPageLength());
-            if (minus && plus)
-                return Overscroll.BOTH;
-            else if (minus)
-                return Overscroll.MINUS;
-            else if (plus)
-                return Overscroll.PLUS;
-            else
-                return Overscroll.NONE;
-        }
-
-        // Returns the amount that the page has been overscrolled. If the page hasn't been
-        // overscrolled on this axis, returns 0.
-        private float getExcess() {
-            switch (getOverscroll()) {
-            case MINUS:     return -getOrigin();
-            case PLUS:      return getViewportEnd() - getPageLength();
-            case BOTH:      return getViewportEnd() - getPageLength() - getOrigin();
-            default:        return 0.0f;
-            }
-        }
-
-        /*
-         * Returns true if the page is zoomed in to some degree along this axis such that scrolling
-         * is possible. Otherwise, returns false.
-         */
-        private boolean scrollable() {
-            return getViewportLength() <= getPageLength() - MIN_SCROLLABLE_DISTANCE;
-        }
-
-        /*
-         * Returns the resistance, as a multiplier, that should be taken into account when
-         * tracking or pinching.
-         */
-        public float getEdgeResistance() {
-            float excess = getExcess();
-            return (excess > 0.0f) ? SNAP_LIMIT - excess / getViewportLength() : 1.0f;
-        }
-
-        /* Returns the velocity. If the axis is locked, returns 0. */
-        public float getRealVelocity() {
-            return locked ? 0.0f : velocity;
-        }
-
-        void startPan() {
-            mFlingState = FlingStates.PANNING;
-        }
-
-        public void startFling(boolean stopped) {
-            disableSnap = mSubscroller.scrolling();
-
-            if (stopped) {
-                mFlingState = FlingStates.STOPPED;
-            } else {
-                mFlingState = FlingStates.FLINGING;
-            }
-        }
-
-        /* Advances a fling animation by one step. */
-        public boolean advanceFling() {
-            if (mFlingState != FlingStates.FLINGING) {
-                return false;
-            }
-
-            float excess = getExcess();
-            if (disableSnap || FloatUtils.fuzzyEquals(excess, 0.0f)) {
-                // If we aren't overscrolled, just apply friction.
-                if (Math.abs(velocity) >= VELOCITY_THRESHOLD) {
-                    velocity *= FRICTION_FAST;
-                } else {
-                    float t = velocity / VELOCITY_THRESHOLD;
-                    velocity *= FloatUtils.interpolate(FRICTION_SLOW, FRICTION_FAST, t);
-                }
-            } else {
-                // Otherwise, decrease the velocity linearly.
-                float elasticity = 1.0f - excess / (getViewportLength() * SNAP_LIMIT);
-                if (getOverscroll() == Overscroll.MINUS)
-                    velocity = Math.min((velocity + OVERSCROLL_DECEL_RATE) * elasticity, 0.0f);
-                else // must be Overscroll.PLUS
-                    velocity = Math.max((velocity - OVERSCROLL_DECEL_RATE) * elasticity, 0.0f);
-            }
-            return true;
-        }
-
-        void stopFling() {
-            velocity = 0.0f;
-            mFlingState = FlingStates.STOPPED;
-        }
-
-        // Performs displacement of the viewport position according to the current velocity.
-        public void displace() {
-            if (!mSubscroller.scrolling() && (locked || !scrollable()))
-                return;
-
-            if (mFlingState == FlingStates.PANNING)
-                displacement += (lastTouchPos - touchPos) * getEdgeResistance();
-            else
-                displacement += velocity;
-        }
-
-        float resetDisplacement() {
-            float d = displacement;
-            displacement = 0.0f;
-            return d;
-        }
-    }
-
     /* Returns the nearest viewport metrics with no overscroll visible. */
     private ViewportMetrics getValidViewportMetrics() {
         ViewportMetrics viewportMetrics = new ViewportMetrics(mController.getViewportMetrics());
         Log.d(LOGTAG, "generating valid viewport using " + viewportMetrics);
 
         /* First, we adjust the zoom factor so that we can make no overscrolled area visible. */
         float zoomFactor = viewportMetrics.getZoomFactor();
         FloatSize pageSize = viewportMetrics.getPageSize();