diff options
author | Paul Rohde <codelogic@google.com> | 2014-12-05 12:17:15 -0800 |
---|---|---|
committer | Gerrit Code Review <gerrit@cyanogenmod.org> | 2016-01-15 10:01:23 -0800 |
commit | d8420e3702c5504e45a51fd29642167b4ab66312 (patch) | |
tree | 6aaebf6a1982a8897966821edc86efb004b079ae /src/com/android/camera/ui/motion | |
parent | db2718622223b230618e9dae5b0c5d2eb4e68741 (diff) | |
download | android_packages_apps_Snap-d8420e3702c5504e45a51fd29642167b4ab66312.tar.gz android_packages_apps_Snap-d8420e3702c5504e45a51fd29642167b4ab66312.tar.bz2 android_packages_apps_Snap-d8420e3702c5504e45a51fd29642167b4ab66312.zip |
Drop new focus indicator into Camera2.
* Create a new custom focus view that interacts with physical lens diopter changes.
* Replace all occurances of the old focus indicator with the new one.
Change-Id: Ia02646ce4d1eb059ecb8a1dfccc15dfc9c167e1b
Diffstat (limited to 'src/com/android/camera/ui/motion')
-rw-r--r-- | src/com/android/camera/ui/motion/AnimationClock.java | 39 | ||||
-rw-r--r-- | src/com/android/camera/ui/motion/DampedSpring.java | 145 | ||||
-rw-r--r-- | src/com/android/camera/ui/motion/DynamicAnimation.java | 41 | ||||
-rw-r--r-- | src/com/android/camera/ui/motion/DynamicAnimator.java | 116 | ||||
-rw-r--r-- | src/com/android/camera/ui/motion/InterpolateUtils.java | 66 | ||||
-rw-r--r-- | src/com/android/camera/ui/motion/InterpolatorHelper.java | 38 | ||||
-rw-r--r-- | src/com/android/camera/ui/motion/Invalidator.java | 28 | ||||
-rw-r--r-- | src/com/android/camera/ui/motion/LinearScale.java | 85 | ||||
-rw-r--r-- | src/com/android/camera/ui/motion/UnitBezier.java | 157 | ||||
-rw-r--r-- | src/com/android/camera/ui/motion/UnitCurve.java | 41 | ||||
-rw-r--r-- | src/com/android/camera/ui/motion/UnitCurves.java | 44 |
11 files changed, 800 insertions, 0 deletions
diff --git a/src/com/android/camera/ui/motion/AnimationClock.java b/src/com/android/camera/ui/motion/AnimationClock.java new file mode 100644 index 000000000..d2504de6b --- /dev/null +++ b/src/com/android/camera/ui/motion/AnimationClock.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +import android.os.SystemClock; + +/** + * Wraps the SystemClock static time methods so they can be exercised in tests. + */ +public abstract class AnimationClock { + + public abstract long getTimeMillis(); + + /** + * Forwards calls to SystemClock.uptimeMillis() since it is the most consistent clock for + * animations. + */ + public static class SystemTimeClock extends AnimationClock { + + @Override + public long getTimeMillis() { + return SystemClock.uptimeMillis(); + } + } +} diff --git a/src/com/android/camera/ui/motion/DampedSpring.java b/src/com/android/camera/ui/motion/DampedSpring.java new file mode 100644 index 000000000..84cbfa6f8 --- /dev/null +++ b/src/com/android/camera/ui/motion/DampedSpring.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +/** + * This models a value after the behavior of a spring. The value tracks the current value, a target + * value, and the current velocity and applies both a directional force and a damping force to the + * value on each update call. + */ +public class DampedSpring { + public static final float DEFAULT_TIME_TO_90_PERCENT_MILLIS = 200.0f; + public static final float DEFAULT_SPRING_STIFFNESS = 3.75f; + public static final float EPSILON = 0.01f; + + private final float mSpringStiffness; + private final float mTimeTo90PercentMs; + + private float mTarget = 0f; + private float mVelocity = 0f; + private float mValue = 0f; + + public DampedSpring() { + this(DEFAULT_TIME_TO_90_PERCENT_MILLIS, DEFAULT_SPRING_STIFFNESS); + } + + public DampedSpring(float timeTo90PercentMs) { + this(timeTo90PercentMs, DEFAULT_SPRING_STIFFNESS); + } + + public DampedSpring(float timeTo90PercentMs, float springStiffness) { + // TODO: Assert timeTo90PercentMs >= 1ms, it might behave badly at low values. + // TODO: Assert springStiffness > 2.0f + + mTimeTo90PercentMs = timeTo90PercentMs; + mSpringStiffness = springStiffness; + + if (springStiffness > timeTo90PercentMs) { + throw new IllegalArgumentException("Creating a spring value with " + + "excessive stiffness will oscillate endlessly."); + } + } + + /** + * @return the current value. + */ + public float getValue() { + return mValue; + } + + /** + * @param value the value to set this instance's current state too. + */ + public void setValue(float value) { + mValue = value; + } + + /** + * @return the current target value. + */ + public float getTarget() { + return mTarget; + } + + /** + * Set a target value. The current value will maintain any existing velocity values and will + * move towards the new target value. To forcibly stopAt the value use the stopAt() method. + * + * @param value the new value to move the current value towards. + */ + public void setTarget(float value) { + mTarget = value; + } + + /** + * Update the current value, moving it towards the actual value over the given + * time delta (in milliseconds) since the last update. This works off of the + * principle of a critically damped spring such that any given current value + * will move elastically towards the target value. The current value maintains + * and applies velocity, acceleration, and a damping force to give a continuous, + * smooth transition towards the target value. + * + * @param dtMs the time since the last update, or zero. + * @return the current value after the update occurs. + */ + public float update(float dtMs) { + float dt = dtMs / mTimeTo90PercentMs; + float dts = dt * mSpringStiffness; + + // If the dts > 1, and the velocity is zero, the force will exceed the + // distance to the target value and it will overshoot the value, causing + // weird behavior and unintended oscillation. since a critically damped + // spring should never overshoot the value, simply the current value to the + // target value. + if (dts > 1.0f || dts < 0.0f) { + stop(); + return mValue; + } + + float delta = (mTarget - mValue); + float force = delta - 2.0f * mVelocity; + + mVelocity += force * dts; + mValue += mVelocity * dts; + + // If we get close enough to the actual value, simply set the current value + // to the current target value and stop. + if (!isActive()) { + stop(); + } + + return mValue; + } + + /** + * @return true if this instance has velocity or it is not at the target value. + */ + public boolean isActive() { + boolean hasVelocity = Math.abs(mVelocity) >= EPSILON; + boolean atTarget = Math.abs(mTarget - mValue) < EPSILON; + return hasVelocity || !atTarget; + } + + /** + * Stop the spring motion wherever it is currently at. Sets target to the + * current value and sets the velocity to zero. + */ + public void stop() { + mTarget = mValue; + mVelocity = 0.0f; + } +} diff --git a/src/com/android/camera/ui/motion/DynamicAnimation.java b/src/com/android/camera/ui/motion/DynamicAnimation.java new file mode 100644 index 000000000..57d5a1021 --- /dev/null +++ b/src/com/android/camera/ui/motion/DynamicAnimation.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +import android.graphics.Canvas; + +/** + * Rendering object that can be driven by an animator instance. + */ +public interface DynamicAnimation { + + /** + * Check to determine if this animation is currently in a stable state. + * + * @return true if the animation is stable, false if it should continue to be redrawn. + */ + boolean isActive(); + + /** + * Update and draw the animation onto the given canvas. + * + * @param t current animation frame time. + * @param dt delta since the last update. + * @param canvas the canvas to draw the animation onto. + */ + void draw(long t, long dt, Canvas canvas); +} diff --git a/src/com/android/camera/ui/motion/DynamicAnimator.java b/src/com/android/camera/ui/motion/DynamicAnimator.java new file mode 100644 index 000000000..542ac1e37 --- /dev/null +++ b/src/com/android/camera/ui/motion/DynamicAnimator.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +import android.graphics.Canvas; + +import java.util.ArrayList; +import java.util.List; + +/** + * Designed to handle the lifecycle of a view that needs a continuous update / + * redraw cycle that does not have a defined start / end time. + * + * Fixed length animations should NOT use this class. + */ +public class DynamicAnimator implements Invalidator { + + public final List<DynamicAnimation> animations = new ArrayList<>(); + + private final Invalidator mInvalidator; + private final AnimationClock mClock; + + private boolean mUpdateRequested = false; + private boolean mIsDrawing = false; + private long mLastDrawTimeMillis = 0; + private long mDrawTimeMillis = 0; + + public DynamicAnimator(Invalidator invalidator, AnimationClock clock) { + mInvalidator = invalidator; + mClock = clock; + } + + public void draw(Canvas canvas) { + mIsDrawing = true; + mUpdateRequested = false; + + mDrawTimeMillis = mClock.getTimeMillis(); + + if (mLastDrawTimeMillis <= 0) { + mLastDrawTimeMillis = mDrawTimeMillis; // On the initial draw, dt is zero. + } + + long dt = mDrawTimeMillis - mLastDrawTimeMillis; + mLastDrawTimeMillis = mDrawTimeMillis; + + // Run the animation + for (DynamicAnimation renderer : animations) { + if (renderer.isActive()) { + renderer.draw(mDrawTimeMillis, dt, canvas); + } + } + + // If either the update or the draw methods requested new frames, then + // invalidate the view which should give us another frame to work with. + // Otherwise, stopAt the last update time. + if (mUpdateRequested) { + mInvalidator.invalidate(); + } else { + mLastDrawTimeMillis = -1; + } + + mIsDrawing = false; + } + + /** + * If a scheduleNewFrame request comes in outside of the animation loop, + * and we didn't schedule a frame after the previous loop (or it's the + * first time we've used this instance), invalidate the view and set the + * last update time to the current time. Theoretically, a few milliseconds + * have elapsed before the view gets updated. + */ + @Override + public void invalidate() { + if (!mIsDrawing && !mUpdateRequested) { + mInvalidator.invalidate(); + mLastDrawTimeMillis = mClock.getTimeMillis(); + } + + mUpdateRequested = true; + } + + /** + * This will return the "best guess" for the most current animation frame + * time. If the loop is currently drawing, then it will return the time the + * draw began, and if an update is currently requested it will return the + * time that the update was requested at, and if neither of these are true + * it will return the current system clock time. + * + * This method will not trigger a new update. + */ + public long getTimeMillis() { + if (mIsDrawing) { + return mDrawTimeMillis; + } + + if (mUpdateRequested) { + return mLastDrawTimeMillis; + } + + return mClock.getTimeMillis(); + } +} diff --git a/src/com/android/camera/ui/motion/InterpolateUtils.java b/src/com/android/camera/ui/motion/InterpolateUtils.java new file mode 100644 index 000000000..3c3cd532f --- /dev/null +++ b/src/com/android/camera/ui/motion/InterpolateUtils.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +/** + * Various static helper functions for interpolating between values. + */ +public class InterpolateUtils { + + private InterpolateUtils() { + } + + /** + * Linear interpolation from v0 to v1 as t goes from 0...1 + * + * @param v0 the value at t=0 + * @param v1 the value at t=1 + * @param t value in the range of 0 to 1. + * @return the value between v0 and v1 as a ratio between 0 and 1 defined by t. + */ + public static float lerp(float v0, float v1, float t) { + return v0 + t * (v1 - v0); + } + + /** + * Project a value that is within the in(Min/Max) number space into the to(Min/Max) number + * space. + * + * @param v value to scale into the 'to' number space. + * @param vMin min value of the values number space. + * @param vMax max value of the values number space. + * @param pMin min value of the projection number space. + * @param pMax max value of the projection number space. + * @return the ratio of the value in the source number space as a value in the to(Min/Max) + * number space. + */ + public static float scale(float v, float vMin, float vMax, float pMin, float pMax) { + return (pMax - pMin) * (v - vMin) / (vMax - vMin) + pMin; + } + + /** + * Value between 0 and 1 as a ratio between tBegin over tDuration + * with no upper bound. + */ + public static float unitRatio(long t, long tBegin, float tDuration) { + if (t <= tBegin) { + return 0.0f; + } + + return (t - tBegin) / tDuration; + } +} diff --git a/src/com/android/camera/ui/motion/InterpolatorHelper.java b/src/com/android/camera/ui/motion/InterpolatorHelper.java new file mode 100644 index 000000000..84114cb03 --- /dev/null +++ b/src/com/android/camera/ui/motion/InterpolatorHelper.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +import android.content.Context; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; + +import com.android.camera.util.ApiHelper; + +public class InterpolatorHelper { + private static Interpolator LINEAR_OUT_SLOW_IN = null; + + public static Interpolator getLinearOutSlowInInterpolator(final Context context) { + if (LINEAR_OUT_SLOW_IN != null) { + return LINEAR_OUT_SLOW_IN; + } + + LINEAR_OUT_SLOW_IN = AnimationUtils.loadInterpolator( + context, android.R.interpolator.linear_out_slow_in); + return LINEAR_OUT_SLOW_IN; + } +} diff --git a/src/com/android/camera/ui/motion/Invalidator.java b/src/com/android/camera/ui/motion/Invalidator.java new file mode 100644 index 000000000..fdb548748 --- /dev/null +++ b/src/com/android/camera/ui/motion/Invalidator.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +/** + * Basic interface for objects that can be invalidated. + */ +public interface Invalidator { + /** + * Request that the object should be redrawn whenever it gets + * the chance. + */ + void invalidate(); +} diff --git a/src/com/android/camera/ui/motion/LinearScale.java b/src/com/android/camera/ui/motion/LinearScale.java new file mode 100644 index 000000000..5886f6882 --- /dev/null +++ b/src/com/android/camera/ui/motion/LinearScale.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +/** + * Represents a discrete linear scale function. + */ +public final class LinearScale { + private final float mDomainA; + private final float mDomainB; + private final float mRangeA; + private final float mRangeB; + + private final float mScale; + + public LinearScale(float domainA, float domainB, float rangeA, float rangeB) { + mDomainA = domainA; + mDomainB = domainB; + mRangeA = rangeA; + mRangeB = rangeB; + + // Precomputed ratio between input domain and output range. + float scale = (mRangeB - mRangeA) / (mDomainB - mDomainA); + mScale = Float.isNaN(scale) ? 0.0f : scale; + } + + /** + * Clamp a given domain value to the given domain. + */ + public float clamp(float domainValue) { + if (mDomainA > mDomainB) { + return Math.max(mDomainB, Math.min(mDomainA, domainValue)); + } + + return Math.max(mDomainA, Math.min(mDomainB, domainValue)); + } + + /** + * Returns true if the value is within the domain. + */ + public boolean isInDomain(float domainValue) { + if (mDomainA > mDomainB) { + return domainValue <= mDomainA && domainValue >= mDomainB; + } + return domainValue >= mDomainA && domainValue <= mDomainB; + } + + /** + * Linearly scale a given domain value into the output range. + */ + public float scale(float domainValue) { + return mRangeA + (domainValue - mDomainA) * mScale; + } + + /** + * For the current domain and range parameters produce a new scale function + * that is the inverse of the current scale function. + */ + public LinearScale inverse() { + return new LinearScale(mRangeA, mRangeB, mDomainA, mDomainB); + } + + @Override + public String toString() { + return "LinearScale{" + + "mDomainA=" + mDomainA + + ", mDomainB=" + mDomainB + + ", mRangeA=" + mRangeA + + ", mRangeB=" + mRangeB + "}"; + } +}
\ No newline at end of file diff --git a/src/com/android/camera/ui/motion/UnitBezier.java b/src/com/android/camera/ui/motion/UnitBezier.java new file mode 100644 index 000000000..242f54556 --- /dev/null +++ b/src/com/android/camera/ui/motion/UnitBezier.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +/** + * This represents is a precomputed cubic bezier curve starting at (0,0) and + * going to (1,1) with two configurable control points. Once the instance is + * created, the control points cannot be modified. + * + * Generally, this will be used for computing timing curves for with control + * points where an x value will be provide from 0.0 - 1.0, and the y value will + * be solved for where y is used as the timing value in some linear + * interpolation of a value. + */ +public class UnitBezier implements UnitCurve { + + private static final float EPSILON = 1e-6f; + + private final DerivableFloatFn mXFn; + private final DerivableFloatFn mYFn; + + /** + * Build and pre-compute a unit bezier. This assumes a starting point of + * (0, 0) and end point of (1.0, 1.0). + * + * @param c0x control point x value for p0 + * @param c0y control point y value for p0 + * @param c1x control point x value for p1 + * @param c1y control point y value for p1 + */ + public UnitBezier(float c0x, float c0y, float c1x, float c1y) { + mXFn = new CubicBezierFn(c0x, c1x); + mYFn = new CubicBezierFn(c0y, c1y); + } + + /** + * Given a unit bezier curve find the height of the curve at t (which is + * internally represented as the xAxis). + * + * @param t the x position between 0 and 1 to solve for y. + * @return the closest approximate height of the curve at x. + */ + @Override + public float valueAt(float t) { + return mYFn.value(solve(t, mXFn)); + } + + /** + * Given a unit bezier curve find a value along the x axis such that + * valueAt(result) produces the input value. + * + * @param value the y position between 0 and 1 to solve for x + * @return the closest approximate input that will produce value when provided + * to the valueAt function. + */ + @Override + public float tAt(float value) { + return mXFn.value(solve(value, mYFn)); + } + + private float solve(float target, DerivableFloatFn fn) { + // For a linear fn, t = value. This makes value a good starting guess. + float input = target; + + // Newton's method (Faster than bisection) + for (int i = 0; i < 8; i++) { + float value = fn.value(input) - target; + if (Math.abs(value) < EPSILON) { + return input; + } + float derivative = fn.derivative(input); + if (Math.abs(derivative) < EPSILON) { + break; + } + input = input - value / derivative; + } + + // Fallback on bi-section + float min = 0.0f; + float max = 1.0f; + input = target; + + if (input < min) { + return min; + } + if (input > max) { + return max; + } + + while (min < max) { + float value = fn.value(input); + if (Math.abs(value - target) < EPSILON) { + return input; + } + + if (target > value) { + min = input; + } else { + max = input; + } + + input = (max - min) * .5f + min; + } + + // Give up, return the closest match we got too. + return input; + } + + private interface DerivableFloatFn { + float value(float x); + float derivative(float x); + } + + /** + * Precomputed constants for a given set of control points along a given + * cubic bezier axis. + */ + private static class CubicBezierFn implements DerivableFloatFn { + private final float c; + private final float a; + private final float b; + + /** + * Build and pre-compute a single axis for a unit bezier. This assumes p0 + * is 0 and p1 is 1. + * + * @param c0 start control point. + * @param c1 end control point. + */ + public CubicBezierFn(float c0, float c1) { + c = 3.0f * c0; + b = 3.0f * (c1 - c0) - c; + a = 1.0f - c - b; + } + + public float value(float x) { + return ((a * x + b) * x + c) * x; + } + public float derivative(float x) { + return (3.0f * a * x + 2.0f * b) * x + c; + } + } +} diff --git a/src/com/android/camera/ui/motion/UnitCurve.java b/src/com/android/camera/ui/motion/UnitCurve.java new file mode 100644 index 000000000..d89f1fa4d --- /dev/null +++ b/src/com/android/camera/ui/motion/UnitCurve.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +/** + * Simple functions that produce values along a curve for any given input and can compute input + * times for a given output value. + */ +public interface UnitCurve { + + /** + * Produce a unit value of this curve at time t. The function should always return a valid + * return value for any valid t input. + * + * @param t ratio of time passed from (0..1) + * @return the unit value at t. + */ + float valueAt(float t); + + /** + * If possible, find a value for t such that valueAt(t) == value or best guess. + * + * @param value to match to the output of valueAt(t) + * @return t where valueAt(t) == value or throw. + */ + float tAt(float value); +} diff --git a/src/com/android/camera/ui/motion/UnitCurves.java b/src/com/android/camera/ui/motion/UnitCurves.java new file mode 100644 index 000000000..a1117fa96 --- /dev/null +++ b/src/com/android/camera/ui/motion/UnitCurves.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +/** + * Predefined material curves and animations. + */ +public class UnitCurves { + public static final UnitCurve FAST_OUT_SLOW_IN = new UnitBezier(0.4f, 0.0f, 0.2f, 1.0f); + public static final UnitCurve LINEAR_OUT_SLOW_IN = new UnitBezier(0.0f, 0.0f, 0.2f, 1.0f); + public static final UnitCurve FAST_OUT_LINEAR_IN = new UnitBezier(0.4f, 0.0f, 1.0f, 1.0f); + public static final UnitCurve LINEAR = new UnitBezier(0.0f, 0.0f, 1.0f, 1.0f); + + /** + * Given two curves (from and to) and a time along the from curve, compute + * the time at t, and find a t along the 'toCurve' that will produce the + * same output. This is useful when interpolating between two different curves + * when the animation is not at the beginning or end. + * + * @param enterCurve the curve to compute the value from + * @param exitCurve the curve to find a time t on that matches output of + * enterCurve at T. + * @param t the time at which to compute the value (0..1) + * @return the time along the exitCurve. + */ + public static float mapEnterCurveToExitCurveAtT(UnitCurve enterCurve, UnitCurve exitCurve, + float t) { + return exitCurve.tAt(1 - enterCurve.valueAt(t)); + } +} |