Calculate a vector from the center of a square to

2020-03-05 02:19发布

问题:

Given a square (described by x, y, width, height) and an angle (in radians) I need to calculate a vector that originates at the squares centre and terminates at the point that collides with the edge of the square at the given angle.

I'm really most interested in the point it collides at so if that would make calculation more efficient let me know.

Can this be generalized to Rectangles? How about polygons in general?

回答1:

The vector will be center + (cos(angle), sin(angle))*magnitude. Given that you want to intersect this with a square, you need to determine magnitude. You can get that with a square with:

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;
}

However, cos(angle) or sin(angle) could be zero, so you should cross multiply that out to get:

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;
}

And you can trivially get the end point from that.

EDIT: Here's a snippet you can drop in place to verify this works with the currently accepted answer:

    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);

Clearly this is applies to an axis aligned rectangle. You can do something similar by finding the closest intersection between the testing vector and every edge in a polygon. (You can optimize that further, but that's left as an exercise to the reader.)



回答2:

EDIT: the correct solution for rectangles. It even does not crash if width or height are zeros! Language: C++.

tan(89.99999) thanks to James Fassett for testing my code.

#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);
}


回答3:

Given the square's width and height you can then determine the center of the square (x+.5w, y+.5h).

From there, you can use some trigonometry to determine the length of the vector line:

tan(angle) = 0.5x / a

where a = the distance between the center of the square and the edge of the square. Your points are then x = a, y = (height).

Please be gentle, as it has been some time since I've used a lot of this math! :-)



回答4:

Edit: There is another working implementation from Pavel now (good dedication from him to put in effort debugging his solution) but I'll leave this here as another alternative that works only for squares (Pavel's works for Rectangles).

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);
}


回答5:

Generalized to rectangles, if a = the angle of vector from the horizontal increasing counter cloclkwise, then the points coordinates can be calculated by the following:

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)

Then coordinates of the point are:

   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);


标签: math vector 2d