forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   Алгоритмика (http://forum.boolean.name/forumdisplay.php?f=21)
-   -   ATAN2 своими руками (http://forum.boolean.name/showthread.php?t=1434)

SubZer0 04.09.2006 18:48

ATAN2 своими руками
 
Привет всем! вот наконец я и добрался рассказать как мучался с такой вещью в JAVAME без матлиб и т.п.

для чего такая байда нужна? обычно нужно чтобы объект научился "смотреть" на другой...

заключается сия функция в расчете углов по двум координатам (а-ля угол вектора)

покопался в нете, нарыл формулу по которой раскладывается атан: atn=32*x*x + 9*y*y;

гы! я тут всем в аську проспамил может думаю какое друое решение есть, а то в этом умножений много... тормозить будет, а я из тех программистов которые раскладывают все формулы на листочке и записывают простейший вариант в код, не люблю когда выдумают жутко громоздкую формулу и потом комп парится их вычисляет... не надо надо мной смеяться придумывая как я бы расписывал массив... я упрощаю все в пределах разумного... :@

так! где я там остановился... ага... атан, мать его! ну короче мучались мы поисками мучались, нифига не нашли, и PAX замутил код:
Код:

public float atan2(float x, float y) {
      float atn,atn1;
      atn1=32*x*x + 9*y*y;
      if (atn1==0) {//если угол перпендикулярен оси х
        if (x>0) {return 90;} else{ return 270; }
      }
          atn=32*(x*y)/atn1;
      if (x>=0) {
        if (y>=0){
          return atn;//первая четверть
        }else{
          return 360-atn;//четвертая четверть
        }
      }else{
        if (y>=0){
          return 90+atn;//вторая четверть
        }else{
          return 270-atn;//третья четверть
        }
      }
    }

но меня это все не устраивало... казалось мне, что можно более простой вариант придумать... тем более что у меня в игре не 360 углов а всего 16... этот код еще преобразовывать надо было бы...

и придумал я свой вариант... чисто специализированный... к другому коду не подойдет...

итак:

сначала рисунок:


пестровато конечно, но попробую объяснить всю суть моей затеи...

по прямой OY координата Y равна нулю, точно также и по ОХ. По синей прямой кордината X будет всегда равна Y... уже половину углов можно описать простейшими условиями, и без вычислений... Но это всеголишь 8 углов... а у меня 16... и я придумал такую весчъ! ;)

придумал делить одну координату на другую и сравнивать с заранее высчитанным результатом... а прежде сравнивать их, если, при положительный координатах, X больше чем Y, то угол будет в коричневом секторе, если наоборот то желтом.

для угла 45 градусов результат будет 1, для угла в 22.5 градуса высчитывается по формуле cos(22,5)/sin(22,5), и все это равно 2,414213562373... теперь мы точно можем сказать, что если результат деления одной координаты на другую получился в пределах от 1 до 2,41, то нужно повергуться на угол гдето между двойкой и тройкой!

но это маленько не то, что нам было нужно... нам нужно знать примерный угол "3"! для этого поделим секторы между (2 и 3) и (3 и 4) еще пополам... таким образом у нас получились лучи расположенные под углами 11.5 и 33.75... высчитаем у них результаты деления одной координаты на другую...

cos(11,25)/sin(11,25)*=*5,027339492126
cos(33,75)/sin(33,75)*=*1,496605762665

вот теперь то, что надо! теперь если результат деления одной координаты на другую будет больше чем 5, то это будет угол 4 (красный сектор), если от 1.49 до 5 то угол 3 (зеленый сектор), а если меньше 1.49, то угол 2...

и такими темпами есь круг... итого требудется всего одно деление и условия (которые обрабатываются значительно быстрее)...

итого: надо выбрать четверть, потом восьмую часть (путем сравнения координат кака больше) и поделить координаты! :)

начал я мутить программу по такому вот кнцепту... и получилось что там еще надо добавить немножко условий, а именно: чтоб исключить деление на ноль, а в остальном все так и осталось как и было:

Код:

                        xp=PositionX-PositionX1;
                        yp=PositionY-PositionY1;
                       
                        xp1=xp; if (xp1<0) xp1=-xp1;
                        yp1=yp; if (yp1<0) yp1=-yp1;
                       
                        if (xp>0) {
                            if (yp>0) {
                                if (xp1>yp1) {
                                    if (yp1==0) angle=12; else {
                                        j=(xp1*16)/yp1;
                                        if (j>=80) angle=12;
                                        if (j<80 && j>=24) angle=13;
                                        if (j<24) angle=14;
                                    }
                                } else {
                                    if (xp1==0) angle=0; else {
                                        j=(yp1*16)/xp1;
                                        if (j>=80) angle=0;
                                        if (j<80 && j>=24) angle=15;
                                        if (j<24) angle=14;
                                    }
                                }
                            } else {
                                if (xp1>yp1) {
                                    if (yp1==0) angle=12; else {
                                        j=(xp1*16)/yp1;
                                        if (j>=80) angle=12;
                                        if (j<80 && j>=24) angle=11;
                                        if (j<24) angle=10;
                                    }
                                } else {
                                    if (xp1==0) angle=8; else {
                                        j=(yp1*16)/xp1;
                                        if (j>=80) angle=8;
                                        if (j<80 && j>=24) angle=9;
                                        if (j<24) angle=10;
                                    }
                                }
                            }
                        } else {
                            if (yp>0) {
                                if (xp1>yp1) {
                                    if (yp1==0) angle=4; else {
                                        j=(xp1*16)/yp1;
                                        if (j>=80) angle=4;
                                        if (j<80 && j>=24) angle=3;
                                        if (j<24) angle=2;
                                    }
                                } else {
                                    if (xp1==0) angle=0; else {
                                        j=(yp1*16)/xp1;
                                        if (j>=80) angle=0;
                                        if (j<80 && j>=24) angle=1;
                                        if (j<24) angle=2;
                                    }
                                }
                            } else {
                                if (xp1>yp1) {
                                    if (yp1==0) angle=4; else {
                                        j=(xp1*16)/yp1;
                                        if (j>=80) angle=4;
                                        if (j<80 && j>=24) angle=5;
                                        if (j<24) angle=6;
                                    }
                                } else {
                                    if (xp1==0) angle=8; else {
                                        j=(yp1*16)/xp1;
                                        if (j>=80) angle=8;
                                        if (j<80 && j>=24) angle=7;
                                        if (j<24) angle=6;
                                    }
                                }
                            }
                           
                        }

в ходе кода встретлся с проблемой, низя юзать десятичные дроби, пэтому пришлось юзать умножение... и сравнивать потом с заранее умноженными вариантами... зато теперь теоретически прога должна выполняться быстрее :@ , на каждом проходе получается - 5 условий, одно умножение и одно деление...

вот так вот... :)

SubZer0 20.07.2007 23:28

Re: ATAN2 своими руками
 
Ну и заключительный штрих:

строчки: j=(yp1*16)/xp1;

заменить на: j=(yp1<<4)/xp1;

и теперь у нас вообще остается 5 условий и одно деление.

:)

Taugeshtu 21.07.2007 16:21

Re: ATAN2 своими руками
 
Ммм, не думаю, что 5 условий и одно деление будут быстрее, чем 4 умножения (которое всё же быстрее деления) и одно сложение. Тест в студию!

SubZer0 21.07.2007 21:55

Re: ATAN2 своими руками
 
в коде PAX'a:

atn1=32*x*x + 9*y*y;
atn=32*(x*y)/atn1;
(итого 6 умножений и одно деление)
плюс 4 условия (на 4 угла)

в моем:

одно деление и 7 условий (на 16 углов)


ИМХО тест не нужен ибо преимущество очевидно...

конечно можно себе представить ситуацию, когда все условия будут неправильно предсказаны предсказателем перехода и соот-но неправильно загружены в кеш, на что уйдут дополнительные такты, а механизм умножения представлен операционным усилителем и за один такт будет происходит ЦАП-Умножение-АЦП, то в этом случае мой механизм может конечно и проиграть, что на мой взгляд маловероятно...


PS Условия считал с точки зрения процессора при выполнении кода...

:)


Часовой пояс GMT +4, время: 09:31.

vBulletin® Version 3.6.5.
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Перевод: zCarot