BouncyDroid.java revision 9bb57352244a7e2db1e1041ebf91ebef692152c0
19bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerpackage com.android.dreamtheater; 29bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 39bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.animation.PropertyValuesHolder; 49bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.animation.TimeAnimator; 59bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.app.Activity; 69bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.content.Context; 79bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.content.Intent; 89bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.dreams.BasicDream; 99bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.graphics.Canvas; 109bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.graphics.Matrix; 119bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.graphics.Paint; 129bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.graphics.RectF; 139bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.os.Bundle; 149bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.util.AttributeSet; 159bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.util.Log; 169bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.view.MotionEvent; 179bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.view.View; 189bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.view.Gravity; 199bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.view.ViewGroup; 209bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.widget.Button; 219bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.widget.FrameLayout; 229bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport android.widget.ImageView; 239bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 249bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport java.util.LinkedList; 259bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerimport java.util.HashMap; 269bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 279bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandlerpublic class BouncyDroid extends Activity { 289bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler static final boolean DEBUG = true; 299bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler static final boolean CENTER_DROID = true; 309bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 319bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public static class BouncyView extends FrameLayout 329bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler { 339bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler boolean mShowDebug = false; 349bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 359bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler static final int RADIUS = 128; 369bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 379bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler static final boolean HAS_INITIAL_IMPULSE = true; 389bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler static final boolean HAS_GRAVITY = true; 399bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler static final boolean HAS_FRICTION = false; 409bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler static final boolean HAS_EDGES = true; 419bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 429bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler static final boolean STICKY_FINGERS = true; 439bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 449bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler static final float MAX_SPEED = 5000f; 459bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 469bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public static class World { 479bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public static final float PX_PER_METER = 100f; 489bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public static final float GRAVITY = 1000f; 499bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public static class Vec { 509bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler float x; 519bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler float y; 529bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public Vec() { 539bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler x = y = 0; 549bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 559bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public Vec(float _x, float _y) { 569bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler x = _x; 579bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler y = _y; 589bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 599bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public Vec add(Vec v) { 609bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler return new Vec(x + v.x, y + v.y); 619bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 629bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public Vec mul(float a) { 639bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler return new Vec(x * a, y * a); 649bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 659bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public Vec sub(Vec v) { 669bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler return new Vec(x - v.x, y - v.y); 679bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 689bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public float mag() { 699bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler return (float) Math.hypot(x, y); 709bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 719bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public Vec norm() { 729bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler float k = 1/mag(); 739bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler return new Vec(x*k, y*k); 749bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 759bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public String toString() { 769bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler return "(" + x + "," + y + ")"; 779bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 789bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 799bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public static class Body { 809bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler float m, r; 819bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler Vec p = new Vec(); 829bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler Vec v = new Vec(); 839bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler LinkedList<Vec> forces = new LinkedList<Vec>(); 849bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler LinkedList<Vec> impulses = new LinkedList<Vec>(); 859bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public Body(float _m, Vec _p) { 869bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler m = _m; 879bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler p = _p; 889bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 899bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public void applyForce(Vec f) { 909bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler forces.add(f); 919bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 929bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public void applyImpulse(Vec f) { 939bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler impulses.add(f); 949bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 959bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public void clearForces() { 969bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler forces.clear(); 979bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 989bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public void removeForce(Vec f) { 999bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler forces.remove(f); 1009bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1019bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public void step(float dt) { 1029bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler p = p.add(v.mul(dt)); 1039bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler for (Vec f : impulses) { 1049bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler v = v.add(f.mul(dt/m)); 1059bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1069bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler impulses.clear(); 1079bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler for (Vec f : forces) { 1089bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler v = v.add(f.mul(dt/m)); 1099bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1109bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1119bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public String toString() { 1129bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler return "Body(m=" + m + " p=" + p + " v=" + v + ")"; 1139bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1149bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1159bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler LinkedList<Body> mBodies = new LinkedList<Body>(); 1169bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public void addBody(Body b) { 1179bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBodies.add(b); 1189bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1199bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 1209bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public void step(float dt) { 1219bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler for (Body b : mBodies) { 1229bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler b.step(dt); 1239bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1249bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1259bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1269bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 1279bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 1289bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler TimeAnimator mAnim; 1299bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler World mWorld; 1309bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler ImageView mBug; 1319bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler View mShowDebugView; 1329bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler HashMap<Integer, World.Vec> mFingers = new HashMap<Integer, World.Vec>(); 1339bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler World.Body mBody; 1349bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler World.Vec mGrabSpot; 1359bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler int mGrabbedPointer = -1; 1369bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 1379bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public BouncyView(Context context, AttributeSet as) { 1389bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler super(context, as); 1399bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 1409bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler /* 1419bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() { 1429bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler @Override 1439bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public void onSystemUiVisibilityChange(int visibility) { 1449bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (visibility == View.STATUS_BAR_VISIBLE) { 1459bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler ((Activity)getContext()).finish(); 1469bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1479bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1489bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler }); 1499bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler */ 1509bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 1519bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBug = new ImageView(context); 1529bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBug.setScaleType(ImageView.ScaleType.MATRIX); 1539bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler addView(mBug, new ViewGroup.LayoutParams( 1549bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler ViewGroup.LayoutParams.WRAP_CONTENT, 1559bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler ViewGroup.LayoutParams.WRAP_CONTENT)); 1569bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 1579bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (DEBUG) { 1589bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler Button b = new Button(getContext()); 1599bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler b.setText("Debugzors"); 1609bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler b.setBackgroundColor(0); // very hard to see! :) 1619bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler b.setOnClickListener(new View.OnClickListener() { 1629bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler @Override 1639bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public void onClick(View v) { 1649bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler setDebug(!mShowDebug); 1659bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1669bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler }); 1679bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler addView(b, new FrameLayout.LayoutParams( 1689bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler ViewGroup.LayoutParams.WRAP_CONTENT, 1699bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler ViewGroup.LayoutParams.WRAP_CONTENT, 1709bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler Gravity.TOP|Gravity.RIGHT)); 1719bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1729bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1739bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 1749bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public void setDebug(boolean d) { 1759bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (d != mShowDebug) { 1769bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (d) { 1779bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mShowDebugView = new DebugView(getContext()); 1789bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mShowDebugView.setLayoutParams( 1799bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler new ViewGroup.LayoutParams( 1809bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler ViewGroup.LayoutParams.MATCH_PARENT, 1819bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler ViewGroup.LayoutParams.MATCH_PARENT 1829bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler )); 1839bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler addView(mShowDebugView); 1849bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 1859bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBug.setBackgroundColor(0x2000FF00); 1869bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } else { 1879bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (mShowDebugView != null) { 1889bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler removeView(mShowDebugView); 1899bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mShowDebugView = null; 1909bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1919bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBug.setBackgroundColor(0); 1929bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1939bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler invalidate(); 1949bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mShowDebug = d; 1959bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1969bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 1979bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 1989bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler private void reset() { 1999bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mWorld = new World(); 2009bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler final float mass = 100; 2019bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBody = new World.Body(mass, new World.Vec(200,200)); 2029bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBody.r = RADIUS; 2039bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mWorld.addBody(mBody); 2049bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mGrabbedPointer = -1; 2059bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 2069bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mAnim = new TimeAnimator(); 2079bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mAnim.setTimeListener(new TimeAnimator.TimeListener() { 2089bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { 2099bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (deltaTime > 0) { 2109bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler int STEPS = 5; 2119bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler final float dt = deltaTime / (float) STEPS; 2129bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler while (STEPS-->0) { 2139bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBody.clearForces(); 2149bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 2159bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (HAS_INITIAL_IMPULSE) { 2169bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler // initial oomph 2179bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (totalTime == 0) { 2189bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBody.applyImpulse(new World.Vec(400000, -200000)); 2199bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 2209bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 2219bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 2229bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (HAS_GRAVITY) { 2239bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler // gravity points down 2249bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBody.applyForce(new World.Vec(0, mass * World.GRAVITY)); 2259bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 2269bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 2279bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (mGrabbedPointer >= 0) { 2289bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler World.Vec finger = mFingers.get(mGrabbedPointer); 2299bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (finger == null) { 2309bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler // let go! 2319bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mGrabbedPointer = -1; 2329bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } else { 2339bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler // never gonna let you go 2349bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler World.Vec newPos = finger.add(mGrabSpot); 2359bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBody.v = mBody.v.add(newPos.sub(mBody.p).mul(dt)); 2369bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBody.p = newPos; 2379bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 2389bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } else { 2399bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler // springs 2409bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler // ideal Hooke's Law plus a maximum force and a minimum length cutoff 2419bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler for (Integer i : mFingers.keySet()) { 2429bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler World.Vec finger = mFingers.get(i); 2439bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler World.Vec springForce = finger.sub(mBody.p); 2449bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler float mag = springForce.mag(); 2459bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 2469bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (STICKY_FINGERS && mag < mBody.r*0.75) { 2479bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler // close enough; we'll call this a "stick" 2489bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mGrabbedPointer = i; 2499bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mGrabSpot = mBody.p.sub(finger); 2509bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBody.v = new World.Vec(0,0); 2519bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler break; 2529bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 2539bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 2549bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler final float SPRING_K = 300000; 2559bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler final float FORCE_MAX = 10*SPRING_K; 2569bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mag = (float) Math.min(mag * SPRING_K, FORCE_MAX); // Hooke's law 2579bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler // float mag = (float) (FORCE_MAX / Math.pow(springForce.mag(), 2)); // Gravitation 2589bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler springForce = springForce.norm().mul(mag); 2599bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBody.applyForce(springForce); 2609bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 2619bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 2629bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 2639bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (HAS_FRICTION) { 2649bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler // sliding friction opposes movement 2659bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBody.applyForce(mBody.v.mul(-4f * mBody.m)); 2669bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 2679bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 2689bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (HAS_EDGES) { 2699bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (mBody.p.x - mBody.r < 0) { 2709bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBody.v.x = (float) Math.abs(mBody.v.x) * 2719bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler (HAS_FRICTION ? 0.7f : 1f); 2729bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } else if (mBody.p.x + mBody.r > getWidth()) { 2739bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBody.v.x = (float) Math.abs(mBody.v.x) * 2749bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler (HAS_FRICTION ? -0.7f : -1f); 2759bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 2769bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (mBody.p.y - mBody.r < 0) { 2779bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBody.v.y = (float) Math.abs(mBody.v.y) * 2789bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler (HAS_FRICTION ? 0.7f : 1f); 2799bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } else if (mBody.p.y + mBody.r > getHeight()) { 2809bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBody.v.y = (float) Math.abs(mBody.v.y) * 2819bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler (HAS_FRICTION ? -0.7f : -1f); 2829bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 2839bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 2849bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 2859bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (MAX_SPEED > 0) { 2869bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (mBody.v.mag() > MAX_SPEED) { 2879bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBody.v = mBody.v.norm().mul(MAX_SPEED); 2889bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 2899bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 2909bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 2919bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler // ok, Euler, do your thing 2929bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mWorld.step(dt / 1000f); // dt is in sec 2939bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 2949bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 2959bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBug.setTranslationX(mBody.p.x - mBody.r); 2969bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBug.setTranslationY(mBody.p.y - mBody.r); 2979bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 2989bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler Matrix m = new Matrix(); 2999bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler m.setScale( 3009bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler (mBody.v.x < 0) ? -1 : 1, 3019bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler (mBody.v.y > 1500) ? -1 : 1, // AAAAAAAAAAAAAAAA 3029bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler RADIUS, RADIUS); 3039bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBug.setImageMatrix(m); 3049bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (CENTER_DROID) { 3059bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mBug.setImageResource( 3069bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler (Math.abs(mBody.v.x) < 25) 3079bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler ? R.drawable.bouncy_center 3089bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler : R.drawable.bouncy); 3099bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 3109bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 3119bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (mShowDebug) mShowDebugView.invalidate(); 3129bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 3139bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler }); 3149bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 3159bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 3169bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler @Override 3179bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler protected void onAttachedToWindow() { 3189bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler super.onAttachedToWindow(); 3199bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler setSystemUiVisibility(View.STATUS_BAR_HIDDEN); 3209bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 3219bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler reset(); 3229bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mAnim.start(); 3239bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 3249bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 3259bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler @Override 3269bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler protected void onDetachedFromWindow() { 3279bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler super.onDetachedFromWindow(); 3289bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mAnim.cancel(); 3299bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 3309bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 3319bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler @Override 3329bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public boolean onTouchEvent(MotionEvent event) { 3339bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler int i; 3349bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler for (i=0; i<event.getPointerCount(); i++) { 3359bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler switch (event.getActionMasked()) { 3369bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler case MotionEvent.ACTION_DOWN: 3379bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler case MotionEvent.ACTION_MOVE: 3389bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler case MotionEvent.ACTION_POINTER_DOWN: 3399bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mFingers.put(event.getPointerId(i), 3409bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler new World.Vec(event.getX(i), event.getY(i))); 3419bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler break; 3429bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 3439bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler case MotionEvent.ACTION_UP: 3449bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler case MotionEvent.ACTION_POINTER_UP: 3459bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mFingers.remove(event.getPointerId(i)); 3469bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler break; 3479bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 3489bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler case MotionEvent.ACTION_CANCEL: 3499bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mFingers.clear(); 3509bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler break; 3519bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 3529bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 3539bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler // expired pointers 3549bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler // for (; i<mFingers.length; i++) { 3559bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler // mFingers[i] = null; 3569bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler // } 3579bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler return true; 3589bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 3599bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 3609bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler @Override 3619bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public boolean isOpaque() { 3629bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler return true; 3639bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 3649bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 3659bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler class DebugView extends View { 3669bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public DebugView(Context ct) { 3679bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler super(ct); 3689bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 3699bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 3709bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler private void drawVector(Canvas canvas, 3719bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler float x, float y, float vx, float vy, 3729bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler Paint pt) { 3739bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler final float mag = (float) Math.hypot(vx, vy); 3749bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 3759bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler canvas.save(); 3769bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler Matrix mx = new Matrix(); 3779bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mx.setSinCos(-vx/mag, vy/mag); 3789bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler mx.postTranslate(x, y); 3799bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler canvas.setMatrix(mx); 3809bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 3819bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler canvas.drawLine(0,0, 0, mag, pt); 3829bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler canvas.drawLine(0, mag, -4, mag-4, pt); 3839bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler canvas.drawLine(0, mag, 4, mag-4, pt); 3849bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 3859bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler canvas.restore(); 3869bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 3879bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 3889bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler @Override 3899bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler protected void onDraw(Canvas canvas) { 3909bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler super.onDraw(canvas); 3919bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 3929bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler Paint pt = new Paint(Paint.ANTI_ALIAS_FLAG); 3939bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler pt.setColor(0xFFCC0000); 3949bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler pt.setTextSize(30f); 3959bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler pt.setStrokeWidth(1.0f); 3969bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 3979bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler for (Integer id : mFingers.keySet()) { 3989bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler World.Vec v = mFingers.get(id); 3999bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler float x = v.x; 4009bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler float y = v.y; 4019bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler pt.setStyle(Paint.Style.FILL); 4029bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler canvas.drawText("#"+id, x+38, y-38, pt); 4039bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler pt.setStyle(Paint.Style.STROKE); 4049bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler canvas.drawLine(x-40, y, x+40, y, pt); 4059bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler canvas.drawLine(x, y-40, x, y+40, pt); 4069bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler canvas.drawCircle(x, y, 40, pt); 4079bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 4089bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler pt.setStyle(Paint.Style.STROKE); 4099bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler if (mBody != null) { 4109bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler float x = mBody.p.x; 4119bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler float y = mBody.p.y; 4129bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler float r = mBody.r; 4139bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler pt.setColor(0xFF6699FF); 4149bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler RectF bounds = new RectF(x-r, y-r, x+r, y+r); 4159bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler canvas.drawOval(bounds, pt); 4169bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 4179bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler pt.setStrokeWidth(3); 4189bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler drawVector(canvas, x, y, mBody.v.x/1000, mBody.v.y/1000, pt); 4199bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 4209bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler pt.setColor(0xFF0033FF); 4219bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler for (World.Vec f : mBody.forces) { 4229bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler drawVector(canvas, x, y, f.x/1000, f.y/1000, pt); 4239bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 4249bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 4259bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 4269bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 4279bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 4289bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler 4299bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler @Override 4309bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler public void onStart() { 4319bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler super.onStart(); 4329bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler setContentView(new BouncyView(this, null)); 4339bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler } 4349bb57352244a7e2db1e1041ebf91ebef692152c0Daniel Sandler} 435