1c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian/* 2c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * Copyright (C) 2010 The Android Open Source Project 3c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * 4c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * Licensed under the Apache License, Version 2.0 (the "License"); 5c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * you may not use this file except in compliance with the License. 6c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * You may obtain a copy of the License at 7c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * 8c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * http://www.apache.org/licenses/LICENSE-2.0 9c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * 10c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * Unless required by applicable law or agreed to in writing, software 11c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * distributed under the License is distributed on an "AS IS" BASIS, 12c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * See the License for the specific language governing permissions and 14c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * limitations under the License. 15c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian */ 16c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian 17c29f1d199f673d470137074face98c2509cd1a16Mathias Agopianpackage com.example.android.accelerometerplay; 18c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian 19c29f1d199f673d470137074face98c2509cd1a16Mathias Agopianimport android.app.Activity; 20c29f1d199f673d470137074face98c2509cd1a16Mathias Agopianimport android.content.Context; 21c29f1d199f673d470137074face98c2509cd1a16Mathias Agopianimport android.graphics.Bitmap; 22c29f1d199f673d470137074face98c2509cd1a16Mathias Agopianimport android.graphics.BitmapFactory; 23c29f1d199f673d470137074face98c2509cd1a16Mathias Agopianimport android.graphics.Canvas; 24c29f1d199f673d470137074face98c2509cd1a16Mathias Agopianimport android.graphics.BitmapFactory.Options; 25c29f1d199f673d470137074face98c2509cd1a16Mathias Agopianimport android.hardware.Sensor; 26c29f1d199f673d470137074face98c2509cd1a16Mathias Agopianimport android.hardware.SensorEvent; 27c29f1d199f673d470137074face98c2509cd1a16Mathias Agopianimport android.hardware.SensorEventListener; 28c29f1d199f673d470137074face98c2509cd1a16Mathias Agopianimport android.hardware.SensorManager; 29c29f1d199f673d470137074face98c2509cd1a16Mathias Agopianimport android.os.Bundle; 30c29f1d199f673d470137074face98c2509cd1a16Mathias Agopianimport android.os.PowerManager; 31c29f1d199f673d470137074face98c2509cd1a16Mathias Agopianimport android.os.PowerManager.WakeLock; 32c29f1d199f673d470137074face98c2509cd1a16Mathias Agopianimport android.util.DisplayMetrics; 33a7c85d92351070605b93176c1f691f4f80321ad5Mathias Agopianimport android.view.Display; 34a7c85d92351070605b93176c1f691f4f80321ad5Mathias Agopianimport android.view.Surface; 35c29f1d199f673d470137074face98c2509cd1a16Mathias Agopianimport android.view.View; 36a7c85d92351070605b93176c1f691f4f80321ad5Mathias Agopianimport android.view.WindowManager; 37c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian 38c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian/** 39c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * This is an example of using the accelerometer to integrate the device's 40c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * acceleration to a position using the Verlet method. This is illustrated with 41c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * a very simple particle system comprised of a few iron balls freely moving on 42c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * an inclined wooden table. The inclination of the virtual table is controlled 43c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * by the device's accelerometer. 44c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * 45c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * @see SensorManager 46c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * @see SensorEvent 47c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian * @see Sensor 48c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian */ 49c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian 50c29f1d199f673d470137074face98c2509cd1a16Mathias Agopianpublic class AccelerometerPlayActivity extends Activity { 51c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian 52a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private SimulationView mSimulationView; 53a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private SensorManager mSensorManager; 54a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private PowerManager mPowerManager; 55a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private WindowManager mWindowManager; 56a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private Display mDisplay; 57a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private WakeLock mWakeLock; 58a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 59a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /** Called when the activity is first created. */ 60a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian @Override 61a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian public void onCreate(Bundle savedInstanceState) { 62a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian super.onCreate(savedInstanceState); 63a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 64a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // Get an instance of the SensorManager 65a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); 66a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 67a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // Get an instance of the PowerManager 68a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mPowerManager = (PowerManager) getSystemService(POWER_SERVICE); 69a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 70a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // Get an instance of the WindowManager 71a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); 72a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mDisplay = mWindowManager.getDefaultDisplay(); 73a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 74a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // Create a bright wake lock 75a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, getClass() 76a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian .getName()); 77a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 78a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // instantiate our simulation view and set it as the activity's content 79a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mSimulationView = new SimulationView(this); 80a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian setContentView(mSimulationView); 81a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 82a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 83a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian @Override 84a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian protected void onResume() { 85a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian super.onResume(); 86a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 87a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * when the activity is resumed, we acquire a wake-lock so that the 88a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * screen stays on, since the user will likely not be fiddling with the 89a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * screen or buttons. 90a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 91a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mWakeLock.acquire(); 92a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 93a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // Start the simulation 94a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mSimulationView.startSimulation(); 95a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 96a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 97a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian @Override 98a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian protected void onPause() { 99a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian super.onPause(); 100a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 101a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * When the activity is paused, we make sure to stop the simulation, 102a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * release our sensor resources and wake locks 103a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 104a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 105a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // Stop the simulation 106a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mSimulationView.stopSimulation(); 107a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 108a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // and release our wake-lock 109a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mWakeLock.release(); 110a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 111a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 112a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian class SimulationView extends View implements SensorEventListener { 113a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // diameter of the balls in meters 114a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private static final float sBallDiameter = 0.004f; 115a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private static final float sBallDiameter2 = sBallDiameter * sBallDiameter; 116a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 117a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // friction of the virtual table and air 118a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private static final float sFriction = 0.1f; 119a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 120a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private Sensor mAccelerometer; 121a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private long mLastT; 122a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mLastDeltaT; 123a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 124a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mXDpi; 125a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mYDpi; 126a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mMetersToPixelsX; 127a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mMetersToPixelsY; 128a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private Bitmap mBitmap; 129a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private Bitmap mWood; 130a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mXOrigin; 131a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mYOrigin; 132a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mSensorX; 133a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mSensorY; 134a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private long mSensorTimeStamp; 135a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private long mCpuTimeStamp; 136a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mHorizontalBound; 137a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mVerticalBound; 138a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private final ParticleSystem mParticleSystem = new ParticleSystem(); 139a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 140a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 141a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * Each of our particle holds its previous and current position, its 142a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * acceleration. for added realism each particle has its own friction 143a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * coefficient. 144a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 145a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian class Particle { 146a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mPosX; 147a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mPosY; 148a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mAccelX; 149a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mAccelY; 150a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mLastPosX; 151a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mLastPosY; 152a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private float mOneMinusFriction; 153a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 154a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian Particle() { 155a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // make each particle a bit different by randomizing its 156a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // coefficient of friction 157a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float r = ((float) Math.random() - 0.5f) * 0.2f; 158a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mOneMinusFriction = 1.0f - sFriction + r; 159a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 160a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 161a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian public void computePhysics(float sx, float sy, float dT, float dTC) { 162a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // Force of gravity applied to our virtual object 163a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float m = 1000.0f; // mass of our virtual object 164a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float gx = -sx * m; 165a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float gy = -sy * m; 166a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 167a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 168a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * �F = mA <=> A = �F / m We could simplify the code by 169a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * completely eliminating "m" (the mass) from all the equations, 170a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * but it would hide the concepts from this sample code. 171a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 172a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float invm = 1.0f / m; 173a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float ax = gx * invm; 174a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float ay = gy * invm; 175a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 176a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 177a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * Time-corrected Verlet integration The position Verlet 178a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * integrator is defined as x(t+�t) = x(t) + x(t) - x(t-�t) + 179a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * a(t)�t�2 However, the above equation doesn't handle variable 180a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * �t very well, a time-corrected version is needed: x(t+�t) = 181a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * x(t) + (x(t) - x(t-�t)) * (�t/�t_prev) + a(t)�t�2 We also add 182a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * a simple friction term (f) to the equation: x(t+�t) = x(t) + 183a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * (1-f) * (x(t) - x(t-�t)) * (�t/�t_prev) + a(t)�t�2 184a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 185a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float dTdT = dT * dT; 186a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float x = mPosX + mOneMinusFriction * dTC * (mPosX - mLastPosX) + mAccelX 187a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * dTdT; 188a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float y = mPosY + mOneMinusFriction * dTC * (mPosY - mLastPosY) + mAccelY 189a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * dTdT; 190a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mLastPosX = mPosX; 191a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mLastPosY = mPosY; 192a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mPosX = x; 193a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mPosY = y; 194a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mAccelX = ax; 195a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mAccelY = ay; 196a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 197a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 198a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 199a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * Resolving constraints and collisions with the Verlet integrator 200a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * can be very simple, we simply need to move a colliding or 201a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * constrained particle in such way that the constraint is 202a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * satisfied. 203a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 204a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian public void resolveCollisionWithBounds() { 205a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float xmax = mHorizontalBound; 206a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float ymax = mVerticalBound; 207a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float x = mPosX; 208a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float y = mPosY; 209a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian if (x > xmax) { 210a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mPosX = xmax; 211a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } else if (x < -xmax) { 212a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mPosX = -xmax; 213a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 214a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian if (y > ymax) { 215a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mPosY = ymax; 216a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } else if (y < -ymax) { 217a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mPosY = -ymax; 218a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 219a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 220a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 221a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 222a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 223a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * A particle system is just a collection of particles 224a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 225a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian class ParticleSystem { 226a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian static final int NUM_PARTICLES = 15; 227a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private Particle mBalls[] = new Particle[NUM_PARTICLES]; 228a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 229a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian ParticleSystem() { 230a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 231a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * Initially our particles have no speed or acceleration 232a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 233a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian for (int i = 0; i < mBalls.length; i++) { 234a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mBalls[i] = new Particle(); 235a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 236a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 237a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 238a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 239a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * Update the position of each particle in the system using the 240a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * Verlet integrator. 241a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 242a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian private void updatePositions(float sx, float sy, long timestamp) { 243a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final long t = timestamp; 244a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian if (mLastT != 0) { 245a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float dT = (float) (t - mLastT) * (1.0f / 1000000000.0f); 246a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian if (mLastDeltaT != 0) { 247a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float dTC = dT / mLastDeltaT; 248a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final int count = mBalls.length; 249a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian for (int i = 0; i < count; i++) { 250a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian Particle ball = mBalls[i]; 251a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian ball.computePhysics(sx, sy, dT, dTC); 252a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 253a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 254a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mLastDeltaT = dT; 255a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 256a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mLastT = t; 257a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 258a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 259a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 260a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * Performs one iteration of the simulation. First updating the 261a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * position of all the particles and resolving the constraints and 262a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * collisions. 263a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 264a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian public void update(float sx, float sy, long now) { 265a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // update the system's positions 266a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian updatePositions(sx, sy, now); 267a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 268a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // We do no more than a limited number of iterations 269a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final int NUM_MAX_ITERATIONS = 10; 270a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 271a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 272a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * Resolve collisions, each particle is tested against every 273a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * other particle for collision. If a collision is detected the 274a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * particle is moved away using a virtual spring of infinite 275a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * stiffness. 276a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 277a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian boolean more = true; 278a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final int count = mBalls.length; 279a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian for (int k = 0; k < NUM_MAX_ITERATIONS && more; k++) { 280a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian more = false; 281a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian for (int i = 0; i < count; i++) { 282a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian Particle curr = mBalls[i]; 283a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian for (int j = i + 1; j < count; j++) { 284a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian Particle ball = mBalls[j]; 285a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian float dx = ball.mPosX - curr.mPosX; 286a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian float dy = ball.mPosY - curr.mPosY; 287a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian float dd = dx * dx + dy * dy; 288a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // Check for collisions 289a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian if (dd <= sBallDiameter2) { 290a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 291a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * add a little bit of entropy, after nothing is 292a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * perfect in the universe. 293a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 294a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian dx += ((float) Math.random() - 0.5f) * 0.0001f; 295a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian dy += ((float) Math.random() - 0.5f) * 0.0001f; 296a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian dd = dx * dx + dy * dy; 297a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // simulate the spring 298a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float d = (float) Math.sqrt(dd); 299a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float c = (0.5f * (sBallDiameter - d)) / d; 300a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian curr.mPosX -= dx * c; 301a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian curr.mPosY -= dy * c; 302a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian ball.mPosX += dx * c; 303a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian ball.mPosY += dy * c; 304a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian more = true; 305a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 306a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 307a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 308a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * Finally make sure the particle doesn't intersects 309a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * with the walls. 310a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 311a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian curr.resolveCollisionWithBounds(); 312a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 313a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 314a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 315a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 316a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian public int getParticleCount() { 317a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian return mBalls.length; 318a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 319a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 320a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian public float getPosX(int i) { 321a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian return mBalls[i].mPosX; 322a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 323a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 324a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian public float getPosY(int i) { 325a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian return mBalls[i].mPosY; 326a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 327a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 328a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 329a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian public void startSimulation() { 330a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 331a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * It is not necessary to get accelerometer events at a very high 332a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * rate, by using a slower rate (SENSOR_DELAY_UI), we get an 333a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * automatic low-pass filter, which "extracts" the gravity component 334a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * of the acceleration. As an added benefit, we use less power and 335a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * CPU resources. 336a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 337a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_UI); 338a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 339a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 340a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian public void stopSimulation() { 341a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mSensorManager.unregisterListener(this); 342a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 343a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 344a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian public SimulationView(Context context) { 345a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian super(context); 346a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 347a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 348a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian DisplayMetrics metrics = new DisplayMetrics(); 349a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian getWindowManager().getDefaultDisplay().getMetrics(metrics); 350a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mXDpi = metrics.xdpi; 351a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mYDpi = metrics.ydpi; 352a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mMetersToPixelsX = mXDpi / 0.0254f; 353a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mMetersToPixelsY = mYDpi / 0.0254f; 354a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 355a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // rescale the ball so it's about 0.5 cm on screen 356a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian Bitmap ball = BitmapFactory.decodeResource(getResources(), R.drawable.ball); 357a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final int dstWidth = (int) (sBallDiameter * mMetersToPixelsX + 0.5f); 358a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final int dstHeight = (int) (sBallDiameter * mMetersToPixelsY + 0.5f); 359a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mBitmap = Bitmap.createScaledBitmap(ball, dstWidth, dstHeight, true); 360a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 361a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian Options opts = new Options(); 362a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian opts.inDither = true; 363a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian opts.inPreferredConfig = Bitmap.Config.RGB_565; 364a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mWood = BitmapFactory.decodeResource(getResources(), R.drawable.wood, opts); 365a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 366a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 367a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian @Override 368a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian protected void onSizeChanged(int w, int h, int oldw, int oldh) { 369a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // compute the origin of the screen relative to the origin of 370a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // the bitmap 371a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mXOrigin = (w - mBitmap.getWidth()) * 0.5f; 372a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mYOrigin = (h - mBitmap.getHeight()) * 0.5f; 373a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mHorizontalBound = ((w / mMetersToPixelsX - sBallDiameter) * 0.5f); 374a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mVerticalBound = ((h / mMetersToPixelsY - sBallDiameter) * 0.5f); 375a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 376a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 377a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian @Override 378a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian public void onSensorChanged(SensorEvent event) { 379a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) 380a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian return; 381a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 382a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * record the accelerometer data, the event's timestamp as well as 383a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * the current time. The latter is needed so we can calculate the 384a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * "present" time during rendering. In this application, we need to 385a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * take into account how the screen is rotated with respect to the 386a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * sensors (which always return data in a coordinate space aligned 387a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * to with the screen in its native orientation). 388a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 389a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 390a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian switch (mDisplay.getRotation()) { 391a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian case Surface.ROTATION_0: 392a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mSensorX = event.values[0]; 393a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mSensorY = event.values[1]; 394a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian break; 395a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian case Surface.ROTATION_90: 396a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mSensorX = -event.values[1]; 397a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mSensorY = event.values[0]; 398a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian break; 399a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian case Surface.ROTATION_180: 400a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mSensorX = -event.values[0]; 401a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mSensorY = -event.values[1]; 402a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian break; 403a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian case Surface.ROTATION_270: 404a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mSensorX = event.values[1]; 405a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mSensorY = -event.values[0]; 406a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian break; 407a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 408a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 409a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mSensorTimeStamp = event.timestamp; 410a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian mCpuTimeStamp = System.nanoTime(); 411a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 412a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 413a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian @Override 414a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian protected void onDraw(Canvas canvas) { 415a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 416a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 417a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * draw the background 418a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 419a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 420a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian canvas.drawBitmap(mWood, 0, 0, null); 421a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 422a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 423a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * compute the new position of our object, based on accelerometer 424a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * data and present time. 425a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 426a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 427a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final ParticleSystem particleSystem = mParticleSystem; 428a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final long now = mSensorTimeStamp + (System.nanoTime() - mCpuTimeStamp); 429a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float sx = mSensorX; 430a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float sy = mSensorY; 431a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 432a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian particleSystem.update(sx, sy, now); 433a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 434a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float xc = mXOrigin; 435a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float yc = mYOrigin; 436a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float xs = mMetersToPixelsX; 437a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float ys = mMetersToPixelsY; 438a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final Bitmap bitmap = mBitmap; 439a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final int count = particleSystem.getParticleCount(); 440a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian for (int i = 0; i < count; i++) { 441a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian /* 442a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * We transform the canvas so that the coordinate system matches 443a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * the sensors coordinate system with the origin in the center 444a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian * of the screen and the unit is the meter. 445a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian */ 446a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 447a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float x = xc + particleSystem.getPosX(i) * xs; 448a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian final float y = yc - particleSystem.getPosY(i) * ys; 449a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian canvas.drawBitmap(bitmap, x, y, null); 450a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 451a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 452a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian // and make sure to redraw asap 453a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian invalidate(); 454a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 455a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian 456a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian @Override 457a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian public void onAccuracyChanged(Sensor sensor, int accuracy) { 458a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 459a37be42f9f4d0f54f8ff10524ee023177f0b7990Mathias Agopian } 460c29f1d199f673d470137074face98c2509cd1a16Mathias Agopian} 461