Рассчитать вектор от центра квадрата к краю на основе радиуса - PullRequest
2 голосов
/ 27 августа 2009

Учитывая квадрат (описываемый x, y, ширина, высота) и угол (в радианах) мне нужно вычислить вектор, который начинается в центре квадратов и заканчивается в точке, которая сталкивается с краем квадрата заданный угол.

Меня больше всего интересует точка, в которой он сталкивается, поэтому, если это сделает расчет более эффективным, дайте мне знать.

Можно ли это обобщить на прямоугольники? Как насчет полигонов в целом?

Ответы [ 5 ]

6 голосов
/ 28 августа 2009

Вектор будет center + (cos(angle), sin(angle))*magnitude. Учитывая, что вы хотите пересечь это с квадратом, вам нужно определить величину. Вы можете получить это с помощью квадрата с:

float abs_cos_angle= fabs(cos(angle));
float abs_sin_angle= fabs(sin(angle));
if (width/2/abs_cos_angle <= height/2/abs_sin_angle)
{
    magnitude= fabs(width/2/abs_cos_angle);
}
else
{
    magnitude= height/2/abs_sin_angle;
}

Однако, cos (угол) или sin (угол) могут быть равны нулю, поэтому вы должны вычесть это умножение, чтобы получить:

float abs_cos_angle= fabs(cos(angle));
float abs_sin_angle= fabs(sin(angle));
if (width/2*abs_sin_angle <= height/2*abs_cos_angle)
{
    magnitude= width/2/abs_cos_angle;
}
else
{
    magnitude= height/2/abs_sin_angle;
}

И вы можете получить тривиальную точку отсюда.

РЕДАКТИРОВАТЬ: Вот фрагмент, который вы можете оставить на месте, чтобы убедиться, что это работает с текущим принятым ответом:

    double magnitude;
    double abs_cos_angle= fabs(cos(angle));
    double abs_sin_angle= fabs(sin(angle));
    if (width/2*abs_sin_angle <= height/2*abs_cos_angle)
    {
        magnitude= width/2/abs_cos_angle;
    }
    else
    {
        magnitude= height/2/abs_sin_angle;
    }

    double check_x= x + cos(angle)*magnitude;
    double check_y= y + sin(angle)*magnitude;

    printf("  a = %d deg: x = %lf; y = %lf\n",(int)(angle/pi*180),check_x,check_y);

Очевидно, что это относится к выровненному по оси прямоугольнику. Вы можете сделать нечто подобное, найдя самое близкое пересечение между вектором тестирования и каждым ребром в многоугольнике. (Вы можете оптимизировать это дальше, но это оставлено читателю в качестве упражнения.)

2 голосов
/ 27 августа 2009

РЕДАКТИРОВАТЬ: правильное решение для прямоугольников. Он даже не падает, если ширина или высота нули! Язык: C ++.

tan (89.99999) благодаря Джеймсу Фассетту за тестирование моего кода.

#include <cstdio>                  
#include <math.h>                  

// declare nonstandard signum function
double sign(double x);                

//define pi because I forgot where it's declared
double const pi = 3.14159;                      

//declare non-standard contangent function      
double cot(double x);                           

int main()                                      
{                                               
    for (double angle = 0.0 ; angle<2*pi; angle += 0.1){
            //angle should be within [0, 2*pi) range    
            //x and y point to the _middle_ of the rectangle
            double x = 0; double y = 0 ;                    
            double width = 1, height = 4;                   
            double base_angle = atan(height/width);         
              // the angle between rectangle diagonal and Ox axis
            double px,py;                                        
            // Which side we're on?                              
            bool left = (fabs(angle - pi) < base_angle);         
            bool right = (angle> 2*pi-base_angle || angle < base_angle);
            bool top = (fabs(angle - pi/2) <= fabs(pi/2 - base_angle)); 
            bool bottom = (fabs(angle - 3*pi/2) <= fabs(pi/2 - base_angle));
            // The helper values used to adjust sides                       
            int lr = (left?-1:0) + (right?1:0);                             
            int tb = (bottom?-1:0) + (top?1:0);                             
            if (lr) {                                                       
                            // we're on vertical edge of rectangle          
                            px = x+width/2*lr;                              
                            py = y+width/2*tan(angle)*lr;
            } else {
                            // we're on the horizontal edge or in the corner
                            px = x+height/2*cot(angle)*tb;
                            py = y+height/2*tb;
            }
            printf("  a = %d deg: x = %lf; y = %lf\n",(int)(angle/pi*180),px,py);
    }

    return 0;
}

// define nonstandard signum function
double sign(double x)
{
    if (x<0) return -1;
    if (x>0) return 1;
    return 0;
}

//define non-standard contangent function
double cot(double x)
{
    return tan(pi/2 - x);
}
0 голосов
/ 28 августа 2009

Редактировать: Сейчас есть еще одна рабочая реализация от Павла (хорошая преданность ему, чтобы приложить усилия для отладки его решения), но я оставлю это здесь как другую альтернативу, которая работает только для квадратов (работы Павла для прямоугольников).

private function calculatePointOnSquare(width:Number, angle:Number):Point
{
    // simple angle wrapping             
    angle = (Math.PI*2 + angle) % (Math.PI*2);

    // calculate a normalized vector from the centre
    // of a square to the edge taking into account
    // the eight possible quadrants
    var myX:Number;
    var myY:Number;
    if(angle < Math.PI/4)
    {
        myX = 1;
        myY = Math.tan(angle);
    }
    else if(angle < Math.PI/2)
    {
        myX = Math.tan(Math.PI/2 - angle);
        myY = 1;
    }
    else if(angle < 3*Math.PI/4)
    {
        myX = -Math.tan(angle - Math.PI/2);
        myY = 1;
    }
    else if(angle < Math.PI)
    {
        myX = -1;
        myY = Math.tan(Math.PI - angle);
    }
    else if(angle < 5*Math.PI/4)
    {
        myX = -1;
        myY = -Math.tan(angle - Math.PI);
    }
    else if(angle < 3*Math.PI/2)
    {
        myX = -Math.tan((3*Math.PI/2) - angle);
        myY = -1;
    }
    else if(angle < 7*Math.PI/4)
    {
        myX = Math.tan(angle - (3*Math.PI/2));
        myY = -1;
    }
    else
    {
        myX = 1;
        myY = -Math.tan(Math.PI*2 - angle);
    }

    // scale and translate the vector
    return new Point(
        (myX * width/2) + width/2, 
        (myY * width/2) + width/2);
}
0 голосов
/ 27 августа 2009

Учитывая ширину и высоту квадрата, вы можете определить его центр (x + .5w, y + .5h).

Оттуда вы можете использовать некоторую тригонометрию для определения длины векторной линии:

tan(angle) = 0.5x / a

где a = расстояние между центром квадрата и краем квадрата. Тогда ваши очки будут x = a, y = (высота).

Пожалуйста, будьте осторожны, так как прошло много времени с тех пор, как я много использовал эту математику! :-)

0 голосов
/ 27 августа 2009

Обобщено на прямоугольники, если a = угол вектора от горизонтального увеличивающегося счетчика по часовой стрелке, то координаты точек могут быть вычислены следующим образом:

let dx = distance from center horizontally, and 
    dy = distance form the center vertically, then 

 dx =  if (tan(a) == 0, then width/2, else Min( height / (2 * tan(a)), width/2)
 dy =  if ABS(a) == Pi/2 then height/2 else  Min( (width/2) * tan(a)),  height/2)

Тогда координаты точки:

   px = (x+width/2) + dx for right quadrants (Pi/2 >= a >= - Pi/2);
      = (x+width/2) - dx for left quadrants  (Pi/2 <= a <= 3Pi/2)
   py = (y+height/2) + dy for lower quadrants (Pi <= a <= 2Pi);
      = (y+height/2) - dy for upper quadrants (0 <= a <= Pi);
...