jeudi 26 février 2015

[Guide] Floating in circular boundary (or rectangular) with accelerometer sensor topic






This guide is about floating, moving a point in coordinate system.

It may be useful to make spirit level (bubble level) or magic 8 ball and so on :)

Just use device's accelerometer sensor to moving a center point.

I wrote android custom view which implements SensorEventListener.

Do some initialization (measuring screen size, set boundary size, assign values...) first.

Draw the x, y axis and boundary and little circle.

In end of the onDraw(), invalidate() makes little circle keep moving.



Important formula to make little circle inside circular boundary is below.


Code:


    private void calc(){
        //for simulating object floating on water
        //against gravity
        xCon += mSensorX;
        yCon -= mSensorY;

        /*
        //for simulating object rolling on ground
        //adjust to gravity
        xCon -= mSensorX;
        yCon += mSensorY;
        */

        //for circular boundary
        if(Math.pow(xCon, 2) + Math.pow(yCon, 2) >= boundarySquare){
            isBoundaryOut = true;


            if(xCon != 0 && yCon != 0){
                radian = (float) Math.atan2(yCon, xCon);
            }


            xCon = (float) (Math.cos(radian) * boundary);
            yCon = (float) (Math.sin(radian) * boundary);

        }
        else{
            isBoundaryOut = false;
        }
    }


add the sensor's value to x, y coordinate (xCon, yCon) and check it is out of the circular boundary.
If it is change the value with the Formula

x = cos(atan2(y, x)) * CIRCULAR_BOUDNARY_RADIUS
y = sin(atan2(y, x)) * CIRCULAR_BOUDNARY_RADIUS

You can select the circle to float on water or roll on ground just change addition <-> subtraction.

- For simulating object floating on water, against gravity
xCon += mSensorX;
yCon -= mSensorY;


- For simulating object rolling on ground, adjust to gravity
xCon -= mSensorX;
yCon += mSensorY;


Also you can change boundary shape easily
For rectangle boundary
if(xCon > horizontalBound){
xCon = horizontalBound;
}
else if(xCon < -horizontalBound){
xCon = -horizontalBound;
}
if(yCon > verticalBound){
yCon = verticalBound;
}
else if(yCon < -verticalBound){
yCon = -verticalBound;
}




Code:


    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
            return;
       
        mSensorX = sensor_weight * event.values[0];
        mSensorY = sensor_weight * event.values[1];
    }


onSensorChanged() I just add sensor values(weighted) to center point x, y and it seems to be quite enough to do rough simulation.

Whole source code of my custom view is like below.


Code:


package net.gerosyab.circularboundary;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.AttributeSet;
import android.view.Display;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;

public class MyView extends View implements SensorEventListener{

    Context context;

    //weight for calculating speed of floating image
    //multiplied with accelrometer sensor value
    //faster if it is more than 1
    //slower if it is less than 1
    float sensor_weight = 2.15f;

    float width;
    float height;
    float cx;
    float cy;
    float x;
    float y;
    float xCon, yCon;
    float boundary;
    float boundarySquare;
    float dotRadius = 15;
    float radian;
    Paint linePaint, circlePaint, dotPaint, textPaint;
    float horizontalBound;
    float verticalBound;
    boolean isBoundaryOut = false;

    private float mSensorX;
    private float mSensorY;
    private SensorManager mSensorManager;
    private Sensor mAccelerometer;
    private WindowManager mWindowManager;
    private Display mDisplay;

    public MyView(Context context) {
        super(context);
        this.context = context;
        init();
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        init();
    }

    public MyView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.context = context;
        init();
    }

    private void init(){
        linePaint = new Paint();
        circlePaint = new Paint();
        dotPaint = new Paint();
        textPaint = new Paint();

        linePaint.setColor(Color.WHITE );
        linePaint.setAntiAlias(true);
        linePaint.setStrokeWidth(2);
        linePaint.setStyle(Paint.Style.STROKE);
        circlePaint.setColor(Color.YELLOW);
        circlePaint.setAntiAlias(true);
        circlePaint.setStrokeWidth(2);
        circlePaint.setStyle(Paint.Style.STROKE);
        dotPaint.setColor(Color.RED);
        dotPaint.setAntiAlias(true);
        dotPaint.setStrokeWidth(5);
        dotPaint.setStyle(Paint.Style.FILL);
        textPaint.setColor(Color.WHITE);
        textPaint.setAntiAlias(true);
        textPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        textPaint.setTextSize(40);

        x = cx;
        y = cy;
        xCon = 0;
        yCon = 0;

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //draw x, y axis
        canvas.drawLine(cx, 0, cx, height, linePaint);
        canvas.drawLine(0, cy, width, cy, linePaint);

        //draw circular boundary
        canvas.drawCircle(cx, cy, boundary, circlePaint);

        canvas.drawRect(cx - horizontalBound, cy - verticalBound, cx + horizontalBound, cy + verticalBound, circlePaint);

        calc();
        //draw dot
        canvas.drawCircle(xCon + cx, yCon + cy, dotRadius, dotPaint);

        //draw text
        canvas.drawText("isBoundaryOut : " + isBoundaryOut, 100, 50, textPaint);
        canvas.drawText("sensorX : " + mSensorX, 100, 100, textPaint);
        canvas.drawText("sensorY : " + mSensorY, 100, 150, textPaint);
        canvas.drawText("xCon : " + xCon, 100, 200, textPaint);
        canvas.drawText("yCon : " + yCon, 100, 250, textPaint);
        canvas.drawText("cx : " + cx, 100, 300, textPaint);
        canvas.drawText("cy : " + cy, 100, 350, textPaint);
        canvas.drawText("horizontalBound : " + horizontalBound, 100, 400, textPaint);
        canvas.drawText("verticalBound : " + verticalBound, 100, 450, textPaint);
        invalidate();
    }

    private void calc(){
        //for simulating object floating on water
        //against gravity
        xCon += mSensorX;
        yCon -= mSensorY;

        /*
        //for simulating object rolling on ground
        //adjust to gravity
        xCon -= mSensorX;
        yCon += mSensorY;
        */

        /*
        //for rectangle boundary
        if(xCon > horizontalBound){
            xCon = horizontalBound;
        }
        else if(xCon < -horizontalBound){
            xCon = -horizontalBound;
        }
        if(yCon > verticalBound){
            yCon = verticalBound;
        }
        else if(yCon < -verticalBound){
            yCon = -verticalBound;
        }
        */

        //for circular boundary
        if(Math.pow(xCon, 2) + Math.pow(yCon, 2) >= boundarySquare){
            isBoundaryOut = true;


            if(xCon != 0 && yCon != 0){
                radian = (float) Math.atan2(yCon, xCon);
            }

            xCon = (float) (Math.cos(radian) * boundary);
            yCon = (float) (Math.sin(radian) * boundary);
        }
        else{
            isBoundaryOut = false;
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getWidth();
        height = getHeight();
        cx = width / 2;
        cy = height / 2;
        boundary = width * 0.15f;
        horizontalBound = boundary;
        verticalBound = boundary;
        boundarySquare = boundary * boundary;

        mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
        mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        // Get an instance of the WindowManager
        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        mDisplay = mWindowManager.getDefaultDisplay();
        mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
            return;

        switch (mDisplay.getRotation()) {
            case Surface.ROTATION_0:
                mSensorX = sensor_weight * event.values[0];
                mSensorY = sensor_weight * event.values[1];
                break;
            case Surface.ROTATION_90:
                mSensorX = sensor_weight * -event.values[1];
                mSensorY = sensor_weight * event.values[0];
                break;
            case Surface.ROTATION_180:
                mSensorX = sensor_weight * -event.values[0];
                mSensorY = sensor_weight * -event.values[1];
                break;
            case Surface.ROTATION_270:
                mSensorX = sensor_weight * event.values[1];
                mSensorY = sensor_weight * -event.values[0];
                break;
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mSensorManager.unregisterListener(this);
    }
}


Hope this is helpful!
Thanks :D








Attached Thumbnails


Click image for larger version<br/><br/>Name:	screeenshot2.png<br/>Views:	N/A<br/>Size:	62.0 KB<br/>ID:	3184507
 

















Aucun commentaire:

Enregistrer un commentaire