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