jjzjj

java - 将旋转对象对应到数值

coder 2023-11-23 原文

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

密码锁上面有数值,这些是纯图形的。

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

在第一个图形中,值应该可以告诉我“0”

在此图形中,用户旋转图像后,该值应该可以告诉我“72”

代码如下:

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 值。

最佳答案

您应该完全重新组织您的代码。一遍又一遍地将新的旋转后乘到矩阵中是一种数值不稳定的计算。最终位图将变得扭曲。尝试从矩阵中检索旋转角度过于复杂且没有必要。

首先注意this是一篇关于绘制位图并围绕选定点旋转的有用的先前文章。

只需维护一个double dialAngle = 0,即刻度盘的当前旋转角度。

您正在做太多工作来检索触摸位置的角度。让 (x0,y0) 成为触摸开始的位置。当时,

// 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) 时,使用此坐标调整表盘相对于初始触摸。然后更新矩阵并重绘:

// 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) 可以完成您使用象限和反正弦所做的所有操作。

要得到当前角度的“刻度”,需要2个pi弧度对应100,所以很简单:

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

要找到作为整数的实际最近刻度,请将分数四舍五入并取模 100。请注意,您可以忽略矩阵!

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

这将始终有效,因为 dialAngle 在 [0..2pi) 中。需要 mod 将舍入值 100 映射回 0。

关于java - 将旋转对象对应到数值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11459300/

有关java - 将旋转对象对应到数值的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  3. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  4. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  5. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  6. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  7. ruby-on-rails - 未在 Ruby 中初始化的对象 - 2

    我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调

  8. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  9. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

    我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

  10. ruby - 一个 YAML 对象可以引用另一个吗? - 2

    我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的ruby​​yaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir

随机推荐