Major improvement to Carousel dragging.
This change adds new drag choices to Carousel:
DRAG_MODEL_PLANE, DRAG_MODEL_CYLINDER_INSIDE and DRAG_MODEL_CYLINDER_OUTSIDE.
The old drag model is still available as DRAG_MODEL_SCREEN_DELTA.
Change-Id: I339c21ceaa493fb302c6b57adebfa2063a68d69a
diff --git a/carousel/java/com/android/ex/carousel/CarouselController.java b/carousel/java/com/android/ex/carousel/CarouselController.java
index fc53642..f6920f1 100644
--- a/carousel/java/com/android/ex/carousel/CarouselController.java
+++ b/carousel/java/com/android/ex/carousel/CarouselController.java
@@ -26,7 +26,6 @@
import android.renderscript.Mesh;
import android.renderscript.RenderScriptGL;
import android.util.Log;
-import android.view.SurfaceHolder;
/**
* <p>
@@ -50,7 +49,6 @@
private RenderScriptGL mRS;
private static final String TAG = "CarouselController";
private static final boolean DBG = false;
- private boolean mTracking;
// These shadow the state of the renderer in case the surface changes so the surface
// can be restored to its previous state.
@@ -86,6 +84,7 @@
private long mFadeInDuration = 250L;
private Bitmap mDetailLoadingBitmap = Bitmap.createBitmap(
new int[] {0}, 0, 1, 1, 1, Bitmap.Config.ARGB_4444);
+ private int mDragModel = CarouselRS.DRAG_MODEL_SCREEN_DELTA;
public CarouselController() {
boolean useDepthBuffer = true;
@@ -122,6 +121,7 @@
setSwaySensitivity(mSwaySensitivity);
setFrictionCoefficient(mFrictionCoefficient);
setDragFactor(mDragFactor);
+ setDragModel(mDragModel);
setLookAt(mEye, mAt, mUp);
setRezInCardCount(mRezInCardCount);
setFadeInDuration(mFadeInDuration);
@@ -494,6 +494,22 @@
}
}
+ /**
+ * Sets the current model for dragging. There are currently four drag models:
+ * {@link CarouselView#DRAG_MODEL_SCREEN_DELTA}
+ * {@link CarouselView#DRAG_MODEL_PLANE}
+ * {@link CarouselView#DRAG_MODEL_CYLINDER_INSIDE}
+ * {@link CarouselView#DRAG_MODEL_CYLINDER_OUTSIDE}
+ *
+ * @param model
+ */
+ public void setDragModel(int model) {
+ mDragModel = model;
+ if (mRenderScript != null) {
+ mRenderScript.setDragModel(model);
+ }
+ }
+
public void setCardRotation(float cardRotation) {
mCardRotation = cardRotation;
if (mRenderScript != null) {
diff --git a/carousel/java/com/android/ex/carousel/CarouselRS.java b/carousel/java/com/android/ex/carousel/CarouselRS.java
index dee79e5..c9c6578 100644
--- a/carousel/java/com/android/ex/carousel/CarouselRS.java
+++ b/carousel/java/com/android/ex/carousel/CarouselRS.java
@@ -48,6 +48,12 @@
public static final int CMD_INVALIDATE_DETAIL_TEXTURE = 610;
public static final int CMD_PING = 1000; // for debugging
+ // Drag models *** THIS LIST MUST MATCH THOSE IN carousel.rs ***
+ public static final int DRAG_MODEL_SCREEN_DELTA = 0;
+ public static final int DRAG_MODEL_PLANE = 1;
+ public static final int DRAG_MODEL_CYLINDER_INSIDE = 2;
+ public static final int DRAG_MODEL_CYLINDER_OUTSIDE = 3;
+
private static final String TAG = "CarouselRS";
private static final int DEFAULT_SLOT_COUNT = 10;
private static final boolean MIPMAP = false;
@@ -242,7 +248,7 @@
}
public void setRadius(float radius) {
- mScript.set_radius(radius);
+ mScript.invoke_setRadius(radius);
}
public void setCardRotation(float cardRotation) {
@@ -265,6 +271,10 @@
mScript.set_dragFactor(dragFactor);
}
+ public void setDragModel(int model) {
+ mScript.set_dragModel(model);
+ }
+
private void initVertexProgram() {
ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null);
mVertexProgram = pvb.create();
diff --git a/carousel/java/com/android/ex/carousel/CarouselView.java b/carousel/java/com/android/ex/carousel/CarouselView.java
index 950c39c..e7e57d4 100644
--- a/carousel/java/com/android/ex/carousel/CarouselView.java
+++ b/carousel/java/com/android/ex/carousel/CarouselView.java
@@ -20,17 +20,12 @@
import com.android.ex.carousel.CarouselRS.CarouselCallback;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.PixelFormat;
-import android.graphics.Bitmap.Config;
-import android.renderscript.FileA3D;
import android.renderscript.Float4;
import android.renderscript.Mesh;
import android.renderscript.RSSurfaceView;
import android.renderscript.RenderScriptGL;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
@@ -45,7 +40,6 @@
public abstract class CarouselView extends RSSurfaceView {
private static final boolean USE_DEPTH_BUFFER = true;
private static final String TAG = "CarouselView";
- private static final boolean DBG = false;
private CarouselRS mRenderScript;
private RenderScriptGL mRS;
private Context mContext;
@@ -53,6 +47,16 @@
CarouselController mController;
+ // Drag relative to x coordinate of motion on screen
+ public static final int DRAG_MODEL_SCREEN_DELTA = CarouselRS.DRAG_MODEL_SCREEN_DELTA;
+ // Drag relative to projected point on plane of carousel
+ public static final int DRAG_MODEL_PLANE = CarouselRS.DRAG_MODEL_PLANE;
+ // Drag relative to projected point on inside (far point) of cylinder centered around carousel
+ public static final int DRAG_MODEL_CYLINDER_INSIDE = CarouselRS.DRAG_MODEL_CYLINDER_INSIDE;
+ // Drag relative to projected point on outside (near point) of cylinder centered around carousel
+ public static final int DRAG_MODEL_CYLINDER_OUTSIDE = CarouselRS.DRAG_MODEL_CYLINDER_OUTSIDE;
+
+
// Note: remember to update carousel.rs when changing the values below
public static class DetailAlignment {
/** Detail is centered vertically with respect to the card **/
@@ -450,6 +454,10 @@
mController.setDragFactor(dragFactor);
}
+ public void setDragModel(int model) {
+ mController.setDragModel(model);
+ }
+
public void setLookAt(float[] eye, float[] at, float[] up) {
mController.setLookAt(eye, at, up);
}
diff --git a/carousel/java/com/android/ex/carousel/carousel.rs b/carousel/java/com/android/ex/carousel/carousel.rs
index 2bcf946..62d113c 100644
--- a/carousel/java/com/android/ex/carousel/carousel.rs
+++ b/carousel/java/com/android/ex/carousel/carousel.rs
@@ -122,6 +122,12 @@
static const int CMD_INVALIDATE_DETAIL_TEXTURE = 610;
static const int CMD_PING = 1000;
+// Drag model *** THIS LIST MUST MATCH THOSE IN CarouselRS.java. ***
+static const int DRAG_MODEL_SCREEN_DELTA = 0; // Drag relative to x coordinate of motion vector
+static const int DRAG_MODEL_PLANE = 1; // Drag relative to projected point on plane of carousel
+static const int DRAG_MODEL_CYLINDER_INSIDE = 2; // Drag relative to point on inside of cylinder
+static const int DRAG_MODEL_CYLINDER_OUTSIDE = 3; // Drag relative to point on outside of cylinder
+
// Constants
static const int ANIMATION_SCALE_TIME = 200; // Time it takes to animate selected card, in ms
static const float3 SELECTED_SCALE_FACTOR = { 0.2f, 0.2f, 0.2f }; // increase by this %
@@ -159,6 +165,7 @@
float rezInCardCount; // this controls how rapidly distant card textures will be rez-ed in
float detailFadeRate; // rate at which details fade as they move into the distance
float4 backgroundColor;
+int dragModel = DRAG_MODEL_SCREEN_DELTA;
rs_program_store programStoreAlphaZ;
rs_program_store programStoreAlphaNoZ;
rs_program_store programStoreNoAlphaZ;
@@ -179,7 +186,7 @@
FragmentShaderConstants* shaderConstants;
rs_sampler linearClamp;
-#pragma rs export_func(createCards, copyCards, lookAt)
+#pragma rs export_func(createCards, copyCards, lookAt, setRadius)
#pragma rs export_func(doStart, doStop, doMotion, doLongPress, doSelection)
#pragma rs export_func(setTexture, setGeometry, setDetailTexture, debugCamera)
#pragma rs export_func(setCarouselRotationAngle)
@@ -207,6 +214,11 @@
0.0f // plane constant (= -dot(P, N))
};
+static Cylinder carouselCylinder = {
+ {0.0f, 0.0f, 0.0f }, // center
+ 1.0f // radius - update with carousel radius.
+};
+
// Because allocations can't have 0 dimensions, we have to track whether or not
// cards are valid separately.
// TODO: Remove this dependency once allocations can have a zero dimension.
@@ -238,6 +250,8 @@
static bool __attribute__((overloadable))
makeRayForPixelAt(Ray* ray, rs_matrix4x4* model, rs_matrix4x4* proj, float x, float y);
static float deltaTimeInSeconds(int64_t current);
+static bool rayPlaneIntersect(Ray* ray, Plane* plane, float* tout);
+static bool rayCylinderIntersect(Ray* ray, Cylinder* cylinder, float* tout);
void init() {
// initializers currently have a problem when the variables are exported, so initialize
@@ -248,7 +262,7 @@
visibleSlotCount = 1;
visibleDetailCount = 3;
bias = 0.0f;
- radius = 1.0f;
+ radius = carouselCylinder.radius = 1.0f;
cardRotation = 0.0f;
cardsFaceTangent = false;
updateCamera = true;
@@ -269,6 +283,11 @@
cardCount = (cardAllocationValid && cardAlloc.p != 0) ? rsAllocationGetDimX(cardAlloc) : 0;
}
+void setRadius(float rad)
+{
+ radius = carouselCylinder.radius = rad;
+}
+
void createCards(int n)
{
if (debugTextureLoading) {
@@ -848,6 +867,7 @@
// Behavior/Physics
////////////////////////////////////////////////////////////////////////////////////////////////////
static int64_t lastTime = 0L; // keep track of how much time has passed between frames
+static float lastAngle = 0.0f;
static float2 lastPosition;
static bool animating = false;
static float velocityThreshold = 0.1f * M_PI / 180.0f;
@@ -858,9 +878,48 @@
static const float G = 9.80f; // gravity constant, in m/s
static const float springConstant = 0.0f;
+// Computes a hit angle from the center of the carousel to a point on either a plane
+// or on a cylinder. If neither is hit, returns false.
+static bool hitAngle(float x, float y, float *angle)
+{
+ Ray ray;
+ makeRayForPixelAt(&ray, &camera, x, y);
+ float t = FLT_MAX;
+ if (dragModel == DRAG_MODEL_PLANE && rayPlaneIntersect(&ray, &carouselPlane, &t)) {
+ const float3 point = (ray.position + t*ray.direction);
+ const float3 direction = point - carouselPlane.point;
+ *angle = atan2(direction.x, direction.z);
+ if (debugSelection) rsDebug("Plane Angle = ", degrees(*angle));
+ return true;
+ } else if ((dragModel == DRAG_MODEL_CYLINDER_INSIDE || dragModel == DRAG_MODEL_CYLINDER_OUTSIDE)
+ && rayCylinderIntersect(&ray, &carouselCylinder, &t)) {
+ const float3 point = (ray.position + t*ray.direction);
+ const float3 direction = point - carouselCylinder.center;
+ *angle = atan2(direction.x, direction.z);
+ if (debugSelection) rsDebug("Cylinder Angle = ", degrees(*angle));
+ return true;
+ }
+ return false;
+}
+
static float dragFunction(float x, float y)
{
- return dragFactor * ((x - lastPosition.x) / rsgGetWidth()) * M_PI;
+ float result;
+ float angle;
+ if (hitAngle(x, y, &angle)) {
+ result = angle - lastAngle;
+ // Handle singularity where atan2 switches between +- PI
+ if (result < -M_PI) {
+ result += 2.0f * M_PI;
+ } else if (result > M_PI) {
+ result -= 2.0f * M_PI;
+ }
+ lastAngle = angle;
+ } else {
+ // If we didn't hit anything or drag model wasn't plane or cylinder, we use screen delta
+ result = dragFactor * ((x - lastPosition.x) / rsgGetWidth()) * M_PI;
+ }
+ return result;
}
static float deltaTimeInSeconds(int64_t current)
@@ -891,6 +950,7 @@
void doStart(float x, float y)
{
touchPosition = lastPosition = (float2) { x, y };
+ lastAngle = hitAngle(x,y, &lastAngle) ? lastAngle : 0.0f;
velocity = 0.0f;
velocityTracker = 0.0f;
velocityTrackerCount = 0;
@@ -1045,14 +1105,14 @@
// Nearest point
const float t1 = (-B - disc) / denom;
- if (t1 > tmin && t1 < *tout) {
+ if (dragModel == DRAG_MODEL_CYLINDER_OUTSIDE && t1 > tmin && t1 < *tout) {
*tout = t1;
return true;
}
// Far point
const float t2 = (-B + disc) / denom;
- if (t2 > tmin && t2 < *tout) {
+ if (dragModel == DRAG_MODEL_CYLINDER_INSIDE && t2 > tmin && t2 < *tout) {
*tout = t2;
return true;
}
diff --git a/carousel/test/src/com/android/carouseltest/CarouselTestActivity.java b/carousel/test/src/com/android/carouseltest/CarouselTestActivity.java
index dfc377a..2d78ae2 100644
--- a/carousel/test/src/com/android/carouseltest/CarouselTestActivity.java
+++ b/carousel/test/src/com/android/carouseltest/CarouselTestActivity.java
@@ -141,6 +141,7 @@
mView.setRezInCardCount(3.0f);
mView.setFadeInDuration(250);
mView.setVisibleDetails(VISIBLE_DETAIL_COUNT);
+ mView.setDragModel(CarouselView.DRAG_MODEL_PLANE);
if (INCREMENTAL_ADD) {
mView.postDelayed(mAddCardRunnable, 2000);
}