Implement new physics model for scroll.
diff --git a/res/raw/rollo.c b/res/raw/rollo.c
index 5ff3308..98e5738 100644
--- a/res/raw/rollo.c
+++ b/res/raw/rollo.c
@@ -5,13 +5,82 @@
#define PI 3.14159f
-float deceleration;
+
+// Attraction to center values from page edge to page center.
+float g_AttractionTable[9];
+float g_FrictionTable[9];
+float g_PhysicsTableSize;
+
+float g_PosPage;
+float g_PosVelocity;
+float g_LastPositionX;
+int g_LastTouchDown;
+float g_DT;
+int g_LastTime;
+int g_Rendering;
+int g_PageCount;
// Drawing constants, should be parameters ======
#define VIEW_ANGLE 1.28700222f
void init() {
- deceleration = 0;
+ g_AttractionTable[0] = 4.5f;
+ g_AttractionTable[1] = 4.5f;
+ g_AttractionTable[2] = 5.0f;
+ g_AttractionTable[3] = 4.0f;
+ g_AttractionTable[4] = -4.0f;
+ g_AttractionTable[5] = -5.0f;
+ g_AttractionTable[6] = -4.5f;
+ g_AttractionTable[7] = -4.5f;
+ g_AttractionTable[8] = -4.5f; // dup 7 to avoid a clamp later
+ g_FrictionTable[0] = 3.5f;
+ g_FrictionTable[1] = 3.6f;
+ g_FrictionTable[2] = 3.7f;
+ g_FrictionTable[3] = 3.8f;
+ g_FrictionTable[4] = 3.8f;
+ g_FrictionTable[5] = 3.7f;
+ g_FrictionTable[6] = 3.6f;
+ g_FrictionTable[7] = 3.5f;
+ g_FrictionTable[8] = 3.5f; // dup 7 to avoid a clamp later
+ g_PhysicsTableSize = 7;
+
+ g_PosVelocity = 0;
+ g_PosPage = 0;
+ g_LastTouchDown = 0;
+ g_LastPositionX = 0;
+}
+
+void clampPosition() {
+ if (g_PosPage < 0) {
+ g_PosPage = 0;
+ g_PosVelocity = 0;
+ }
+ if (g_PosPage > (g_PageCount - 1)) {
+ g_PosPage = (g_PageCount - 1);
+ g_PosVelocity = 0;
+ }
+}
+
+void move() {
+ if (g_LastTouchDown) {
+ float dx = -(state->newPositionX - g_LastPositionX);
+ g_PosVelocity = 0;
+ g_PosPage += dx;
+ }
+ g_LastTouchDown = state->newTouchDown;
+ g_LastPositionX = state->newPositionX;
+ clampPosition();
+}
+
+void fling() {
+ g_LastTouchDown = 0;
+ g_PosVelocity = -state->flingVelocityX;
+ if (g_PosPage <= 0) {
+ g_PosVelocity = maxf(0, g_PosVelocity);
+ }
+ if (g_PosPage > (g_PageCount - 1)) {
+ g_PosVelocity = minf(0, (g_PageCount - 1) - g_PosPage);
+ }
}
int g_lastFrameTime = 0;
@@ -41,6 +110,76 @@
return x-(y*floorf(x/y));
}
+void updatePos() {
+ if (g_LastTouchDown) {
+ return;
+ }
+
+ //debugF("g_PosPage", g_PosPage);
+ //debugF(" g_PosVelocity", g_PosVelocity);
+
+ float tablePosNorm = fracf(g_PosPage + 0.5f);
+ float tablePosF = tablePosNorm * g_PhysicsTableSize;
+ int tablePosI = tablePosF;
+ float tablePosFrac = tablePosF - tablePosI;
+ //debugF("tablePosNorm", tablePosNorm);
+ //debugF("tablePosF", tablePosF);
+ //debugF("tablePosI", tablePosI);
+ //debugF("tablePosFrac", tablePosFrac);
+
+ float accel = lerpf(g_AttractionTable[tablePosI],
+ g_AttractionTable[tablePosI + 1],
+ tablePosFrac) * g_DT;
+ float friction = lerpf(g_FrictionTable[tablePosI],
+ g_FrictionTable[tablePosI + 1],
+ tablePosFrac) * g_DT;
+ //debugF(" accel", accel);
+ //debugF(" friction", friction);
+
+ g_PosVelocity += accel;
+ if ((friction > fabsf(g_PosVelocity)) && (friction > fabsf(accel))) {
+ // Special get back to center and overcome friction physics.
+ float t = tablePosNorm - 0.5f;
+ if (fabsf(t) < (friction * g_DT)) {
+ // really close, just snap
+ g_PosPage = roundf(g_PosPage);
+ g_PosVelocity = 0;
+ } else {
+ if (t > 0) {
+ g_PosVelocity = -friction;
+ } else {
+ g_PosVelocity = friction;
+ }
+ }
+ } else {
+ // Normal physics
+ if (g_PosVelocity > 0) {
+ g_PosVelocity -= friction;
+ if (g_PosVelocity < 0) {
+ g_PosVelocity = 0;
+ }
+ } else {
+ g_PosVelocity += friction;
+ if (g_PosVelocity > 0) {
+ g_PosVelocity = 0;
+ }
+ }
+ }
+ g_PosPage += g_PosVelocity * g_DT;
+
+ // Check for out of boundry conditions.
+ if (g_PosPage < 0 && g_PosVelocity < 0) {
+ float damp = 1.0 + (g_PosPage * 3);
+ damp = clampf(damp, 0.f, 0.9f);
+ g_PosVelocity *= damp;
+ }
+ if (g_PosPage > (g_PageCount-1) && g_PosVelocity > 0) {
+ float damp = 1.0 - ((g_PosPage - g_PageCount + 1) * 3);
+ damp = clampf(damp, 0.f, 0.9f);
+ g_PosVelocity *= damp;
+ }
+}
+
float
far_size(float sizeAt0)
{
@@ -53,6 +192,7 @@
int row;
int col;
+ //debugF("center angle", centerAngle);
float scale = 1.0f - state->zoom;
float iconTextureWidth = ICON_WIDTH_PX / (float)ICON_TEXTURE_WIDTH_PX;
@@ -142,6 +282,11 @@
// Clear to transparent
pfClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ int newTime = uptimeMillis();
+ g_DT = (newTime - g_LastTime) / 1000.f;
+ g_LastTime = newTime;
+ //debugF("*** dt ", g_DT);
+
// If we're not supposed to be showing, don't do anything.
if (!state->visible) {
return 0;
@@ -149,128 +294,13 @@
// icons & labels
int iconCount = state->iconCount;
- int pageCount = count_pages(iconCount);
+ g_PageCount = count_pages(iconCount);
- float scrollXPx = state->scrollX;
- float maxScrollXPx = -(pageCount-1) * SCREEN_WIDTH_PX;
- int done = 0;
+ updatePos(0.1f);
+ state->readPosX = g_PosPage;
+ state->readVel = g_PosVelocity;
- // Clamp -- because java doesn't know how big the icons are
- if (scrollXPx > 0) {
- scrollXPx = 0;
- }
- if (scrollXPx < maxScrollXPx) {
- scrollXPx = maxScrollXPx;
- }
-
- // If we've been given a velocity, start a fling
- float flingVelocityPxMs = state->flingVelocityX;
- if (flingVelocityPxMs != 0) {
- // how many screens will this velocity do? TODO: use long
- // G * ppi * friction // why G? // friction = 0.015
- float flingDurationMs;
- if (deceleration == 0) {
- // On the first frame, calculate which animation we're going to do. If it's
- // going to end up less than halfway into a page, we'll bounce back the previous
- // page. Otherwise, we'll adjust the deceleration so it just makes it to the
- // page boundary.
- if (flingVelocityPxMs > 0) {
- deceleration = -1000;
- } else {
- deceleration = 1000;
- }
- // minimum velocity
- if (flingVelocityPxMs < 0) {
- if (flingVelocityPxMs > -500) {
- flingVelocityPxMs = -500;
- }
- } else {
- if (flingVelocityPxMs < 500) {
- flingVelocityPxMs = 500;
- }
- }
-
- // v' = v + at --> t = -v / a
- // x' = x + vt + .5 a t^2
- flingDurationMs = - flingVelocityPxMs / deceleration;
- float endPos = scrollXPx + (flingVelocityPxMs*flingDurationMs)
- + ((deceleration*flingDurationMs*flingDurationMs)/2);
-
- if (endPos > 0) {
- endPos = 0;
- }
- if (endPos < maxScrollXPx) {
- endPos = maxScrollXPx;
- }
- float scrollOnPage = modf(endPos, SCREEN_WIDTH_PX);
- int endPage = -endPos/SCREEN_WIDTH_PX;
-
- if (flingVelocityPxMs < 0) {
- if (scrollOnPage < (SCREEN_WIDTH_PX/2)) {
- // adjust the deceleration so we align on the page boundary
- // a = 2(x-x0-v0t)/t^2
- endPos = -(endPage+1) * SCREEN_WIDTH_PX;
- debugI32("endPos case 1", endPos);
- } else {
- // TODO: bounce
- endPos = -(endPage+1) * SCREEN_WIDTH_PX;
- debugI32("endPos case 2", endPos);
- }
- } else {
- if (scrollOnPage >= (SCREEN_WIDTH_PX/2)) {
- // adjust the deceleration so we align on the page boundary
- endPos = -endPage * SCREEN_WIDTH_PX;
- debugI32("endPos case 3", endPos);
- } else {
- // TODO: bounce
- endPos = -endPage * SCREEN_WIDTH_PX;
- debugI32("endPos case 4", endPos);
- }
- }
- // v = v0 + at --> (v - v0) / t
- deceleration = 2*(endPos-scrollXPx-(flingVelocityPxMs*flingDurationMs))
- / (flingDurationMs*flingDurationMs);
- endPos = scrollXPx + (flingVelocityPxMs*flingDurationMs)
- + ((deceleration*flingDurationMs*flingDurationMs)/2);
-
- state->flingDuration = flingDurationMs;
- state->flingEndPos = endPos;
- } else {
- flingDurationMs = state->flingDuration;
- }
-
- // adjust the deceleration so we always hit a page boundary
-
- int now = uptimeMillis();
- float elapsedTime = (now - state->flingTimeMs) / 1000.0f;
- int animEndTime = -flingVelocityPxMs / deceleration;
-
- int flingOffsetPx = (flingVelocityPxMs * elapsedTime)
- + (deceleration * elapsedTime * elapsedTime / 2.0f);
- scrollXPx += flingOffsetPx;
-
- if (elapsedTime > flingDurationMs) {
- scrollXPx = state->flingEndPos;
- done = 1;
- }
- } else {
- done = 1;
- }
-
- // Clamp
- if (scrollXPx > 0) {
- scrollXPx = 0;
- }
- if (scrollXPx < maxScrollXPx) {
- scrollXPx = maxScrollXPx;
- }
-
- state->currentScrollX = scrollXPx;
- if (done) {
- state->scrollX = scrollXPx;
- state->flingVelocityX = 0;
- deceleration = 0.f;
- }
+ //debugF(" draw g_PosPage", g_PosPage);
// Draw the icons ========================================
bindProgramVertex(NAMED_PV);
@@ -282,9 +312,8 @@
int lastIcon = iconCount-1;
- float currentPage = -scrollXPx / (float)SCREEN_WIDTH_PX;
- int page = currentPage;
- float currentPagePosition = currentPage - page;
+ int page = g_PosPage;
+ float currentPagePosition = g_PosPage - page;
int iconsPerPage = COLUMNS_PER_PAGE * ROWS_PER_PAGE;
int icon = clamp(iconsPerPage * page, 0, lastIcon);
@@ -292,6 +321,7 @@
draw_page(icon, lastIcon, -VIEW_ANGLE*currentPagePosition);
draw_page(icon+iconsPerPage, lastIcon, (-VIEW_ANGLE*currentPagePosition)+VIEW_ANGLE);
+
// Draw the border lines for debugging ========================================
/*
bindProgramVertex(NAMED_PVOrtho);
@@ -320,8 +350,13 @@
drawRect(handleLeft, handleTop, handleLeft+handleWidth, handleTop+handleHeight, 0.0f);
*/
- print_frame_rate();
+ //print_frame_rate();
- return !done;
+ // Bug workaround where the last frame is not always displayed
+ // So we render the last frame twice.
+ int rendering = g_Rendering;
+ g_Rendering = (g_PosVelocity != 0) || fracf(g_PosPage);
+ rendering |= g_Rendering;
+ return rendering;
}