对应旋转的对象为数字值 [英] Corresponding rotated object to numeric values

查看:116
本文介绍了对应旋转的对象为数字值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个360度圆形的组合锁旋转。

I have a combination lock rotating in a 360 degrees circle.

组合锁上有数值,这是纯粹的图形。

The combination lock has numerical values on it, these are purely graphical.

我需要一种方法来对图像的旋转转换为对图形0-99值。

I need a way to translate the image's rotation to the 0-99 values on the graphic.

在此第一图形,该值应能告诉我0

In this first graphic, the value should be able to tell me "0"

在此图形,在用户已经旋转图像,该值应该能够告诉我,72

In this graphic, after the user has rotated the image, the value should be able to tell me "72"

下面是code:

package co.sts.combinationlock;

import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.View.OnTouchListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.ImageView;
import android.support.v4.app.NavUtils;

public class ComboLock extends Activity{

        private static Bitmap imageOriginal, imageScaled;
        private static Matrix matrix;

        private ImageView dialer;
        private int dialerHeight, dialerWidth;

        private GestureDetector detector;

        // needed for detecting the inversed rotations
        private boolean[] quadrantTouched;

        private boolean allowRotating;

        @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_combo_lock);

        // load the image only once
        if (imageOriginal == null) {
                imageOriginal = BitmapFactory.decodeResource(getResources(), R.drawable.numbers);
        }

        // initialize the matrix only once
        if (matrix == null) {
                matrix = new Matrix();
        } else {
                // not needed, you can also post the matrix immediately to restore the old state
                matrix.reset();
        }

        detector = new GestureDetector(this, new MyGestureDetector());

        // there is no 0th quadrant, to keep it simple the first value gets ignored
        quadrantTouched = new boolean[] { false, false, false, false, false };

        allowRotating = true;

        dialer = (ImageView) findViewById(R.id.locknumbers);
        dialer.setOnTouchListener(new MyOnTouchListener());
        dialer.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

                @Override
                        public void onGlobalLayout() {
                        // method called more than once, but the values only need to be initialized one time
                        if (dialerHeight == 0 || dialerWidth == 0) {
                                dialerHeight = dialer.getHeight();
                                dialerWidth = dialer.getWidth();

                                // resize
                                        Matrix resize = new Matrix();
                                        //resize.postScale((float)Math.min(dialerWidth, dialerHeight) / (float)imageOriginal.getWidth(), (float)Math.min(dialerWidth, dialerHeight) / (float)imageOriginal.getHeight());
                                        imageScaled = Bitmap.createBitmap(imageOriginal, 0, 0, imageOriginal.getWidth(), imageOriginal.getHeight(), resize, false);

                                        // translate to the image view's center
                                        float translateX = dialerWidth / 2 - imageScaled.getWidth() / 2;
                                        float translateY = dialerHeight / 2 - imageScaled.getHeight() / 2;
                                        matrix.postTranslate(translateX, translateY);

                                        dialer.setImageBitmap(imageScaled);
                                        dialer.setImageMatrix(matrix);
                        }
                        }
                });

    }

        /**
         * Rotate the dialer.
         *
         * @param degrees The degrees, the dialer should get rotated.
         */
        private void rotateDialer(float degrees) {
                matrix.postRotate(degrees, dialerWidth / 2, dialerHeight / 2);

                //need to print degrees

                dialer.setImageMatrix(matrix);
        }

        /**
         * @return The angle of the unit circle with the image view's center
         */
        private double getAngle(double xTouch, double yTouch) {
                double x = xTouch - (dialerWidth / 2d);
                double y = dialerHeight - yTouch - (dialerHeight / 2d);

                switch (getQuadrant(x, y)) {
                        case 1:
                                return Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;

                        case 2:
                        case 3:
                                return 180 - (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);

                        case 4:
                                return 360 + Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;

                        default:
                                // ignore, does not happen
                                return 0;
                }
        }

        /**
         * @return The selected quadrant.
         */
        private static int getQuadrant(double x, double y) {
                if (x >= 0) {
                        return y >= 0 ? 1 : 4;
                } else {
                        return y >= 0 ? 2 : 3;
                }
        }

        /**
         * Simple implementation of an {@link OnTouchListener} for registering the dialer's touch events.
         */
        private class MyOnTouchListener implements OnTouchListener {

                private double startAngle;

                @Override
                public boolean onTouch(View v, MotionEvent event) {

                        switch (event.getAction()) {

                                case MotionEvent.ACTION_DOWN:

                                        // reset the touched quadrants
                                        for (int i = 0; i < quadrantTouched.length; i++) {
                                                quadrantTouched[i] = false;
                                        }

                                        allowRotating = false;

                                        startAngle = getAngle(event.getX(), event.getY());
                                        break;

                                case MotionEvent.ACTION_MOVE:
                                        double currentAngle = getAngle(event.getX(), event.getY());
                                        rotateDialer((float) (startAngle - currentAngle));
                                        startAngle = currentAngle;
                                        break;

                                case MotionEvent.ACTION_UP:
                                        allowRotating = true;
                                        break;
                        }

                        // set the touched quadrant to true
                        quadrantTouched[getQuadrant(event.getX() - (dialerWidth / 2), dialerHeight - event.getY() - (dialerHeight / 2))] = true;

                        detector.onTouchEvent(event);

                        return true;
                }
        }

        /**
         * Simple implementation of a {@link SimpleOnGestureListener} for detecting a fling event.
         */
        private class MyGestureDetector extends SimpleOnGestureListener {
                @Override
                public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

                        // get the quadrant of the start and the end of the fling
                        int q1 = getQuadrant(e1.getX() - (dialerWidth / 2), dialerHeight - e1.getY() - (dialerHeight / 2));
                        int q2 = getQuadrant(e2.getX() - (dialerWidth / 2), dialerHeight - e2.getY() - (dialerHeight / 2));

                        // the inversed rotations
                        if ((q1 == 2 && q2 == 2 && Math.abs(velocityX) < Math.abs(velocityY))
                                        || (q1 == 3 && q2 == 3)
                                        || (q1 == 1 && q2 == 3)
                                        || (q1 == 4 && q2 == 4 && Math.abs(velocityX) > Math.abs(velocityY))
                                        || ((q1 == 2 && q2 == 3) || (q1 == 3 && q2 == 2))
                                        || ((q1 == 3 && q2 == 4) || (q1 == 4 && q2 == 3))
                                        || (q1 == 2 && q2 == 4 && quadrantTouched[3])
                                        || (q1 == 4 && q2 == 2 && quadrantTouched[3])) {

                                dialer.post(new FlingRunnable(-1 * (velocityX + velocityY)));
                        } else {
                                // the normal rotation
                                dialer.post(new FlingRunnable(velocityX + velocityY));
                        }

                        return true;
                }
        }

        /**
         * A {@link Runnable} for animating the the dialer's fling.
         */
        private class FlingRunnable implements Runnable {

                private float velocity;

                public FlingRunnable(float velocity) {
                        this.velocity = velocity;
                }

                @Override
                public void run() {
                        if (Math.abs(velocity) > 5 && allowRotating) {
                                //rotateDialer(velocity / 75);
                                //velocity /= 1.0666F;

                                // post this instance again
                                dialer.post(this);
                        }
                }
        }
}

我想我需要翻译从矩阵的一些信息到0-99值。

I think I need to translate some information from the matrix to a 0-99 value.

推荐答案

您应该彻底重新组织你的code。在后乘新转成一个矩阵,一遍又一遍的一个数值不稳定计算。最终,位图将变得扭曲。尝试从基质中的旋转角度太复杂的和不必要的。

You should reorganize your code completely. Post-multiplying new rotations into a matrix over and over again is a numerically unstable computation. Eventually the bitmap will become distorted. Trying to retrieve the rotation angle from the matrix is too complex and unnecessary.

首先要注意<一href="http://stackoverflow.com/questions/4166917/android-how-to-rotate-a-bitmap-on-a-center-point">this是位图绘制带绕在所选位置用之前的文章。

First note that this is a useful prior article on drawing bitmaps with rotation about a chosen point.

只是保持一个双dialAngle = 0 就是在表盘的当前旋转角度。

Just maintain a single double dialAngle = 0 that is the current rotation angle of the dial.

您正在做太多的工作来获取触摸位置的角度。让(X0,Y0)是触摸开始的位置。在那个时候,

You are doing way too much work to retrieve the angle from the touch location. Let (x0,y0) be the location where the touch starts. At that time,

// Record the angle at initial touch for use in dragging.
dialAngleAtTouch = dialAngle;
// Find angle from x-axis made by initial touch coordinate.
// y-coordinate might need to be negated due to y=0 -> screen top. 
// This will be obvious during testing.
a0 = Math.atan2(y0 - yDialCenter, x0 - xDialCenter);

这是起始角度。当触摸拖动到(X,Y),使用该坐标相对于初始触摸调节旋钮。然后更新矩阵和重绘:

This is the starting angle. When the touch drags to (x,y), use this coordinate to adjust the dial with respect to the initial touch. Then update the matrix and redraw:

// Find new angle to x-axis. Same comment as above on y coord.
a = Math.atan2(y - yDialCenter, x - xDialCenter);
// New dial angle is offset from the one at initial touch.
dialAngle = dialAngleAtTouch + (a - a0); 
// normalize angles to the interval [0..2pi)
while (dialAngle < 0) dialAngle += 2 * Math.PI;
while (dialAngle >= 2 * Math.PI) dialAngle -= 2 * Math.PI;

// Set the matrix for every frame drawn. Matrix API has a call
// for rotation about a point. Use it!
matrix.setRotate((float)dialAngle * (180 / 3.1415926f), xDialCenter, yDialCenter);

// Invalidate the view now so it's redrawn in with the new matrix value.

请注意 Math.atan2(Y,X)做了所有你正在做的有象限和arcsines。什么

Note Math.atan2(y, x) does all of what you're doing with quadrants and arcsines.

要得到当前角度的滴答,则需要2 PI弧度对应于100,所以这是非常简单的:

To get the "tick" of the current angle, you need 2 pi radians to correspond to 100, so it's very simple:

double fractionalTick = dialAngle / (2 * Math.Pi) * 100;

要找到实际的最近的刻度为整数,圆形的分数和MOD由100注意,你可以忽略矩阵!

To find the actual nearest tick as an integer, round the fraction and mod by 100. Note you can ignore the matrix!

 int tick = (int)(fractionalTick + 0.5) % 100;

这永远是可行的,因为 dialAngle 在[0..2pi)。mod是需要的100回舍入的值映射到0。

This will always work because dialAngle is in [0..2pi). The mod is needed to map a rounded value of 100 back to 0.

这篇关于对应旋转的对象为数字值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆