Calculate a vector from the center of a square to edge based on radius

前端 未结 5 1893
Happy的楠姐
Happy的楠姐 2021-01-06 12:58

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 th

相关标签:
5条回答
  • 2021-01-06 13:29

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

    0 讨论(0)
  • 2021-01-06 13:37

    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! :-)

    0 讨论(0)
  • 2021-01-06 13:50

    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);
    
    0 讨论(0)
  • 2021-01-06 13:51

    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);
    }
    
    0 讨论(0)
  • 2021-01-06 13:56

    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);
    }
    
    0 讨论(0)
提交回复
热议问题