calculating distance between a point and a rectangular box (nearest point)

前端 未结 9 1731
广开言路
广开言路 2020-12-13 06:25

is there a easy formula to calculate this? i\'ve been working on some math but i can only find a way to calculate the distance directed to the center of the box, not direct

相关标签:
9条回答
  • 2020-12-13 06:52

    Kikito's answer is not correct, in fact if P is in the regions 2, 4, 5 or 7 of Ted Hopp's scheme, it returns the minimum distance from the vertices, which is different (bigger) from the minimum distance from the edges.

    I would fix kikito's function distance_aux by returning 0 instead of min(p - lower, upper - p), and everything works apart from the 0 region where P is inside the box. In my opinion that region should be managed separately, depending on what you want to achieve, whether the distance from the area or the distance from the perimeter of the box. If you want to obtain the distance from the area of the box, I would say that it is zero when the point is inside the box.

    function inside(point, box)
        return (point.x > box.left AND point.x < box.right AND point.y > box.top AND point.y < box.bottom)
    end
    
    function distance_aux(p, lower, upper)
        if p < lower then return lower - p end
        if p > upper then return p - upper end
        return 0
    end
    
    function distance(point, box)
        local dx = distance_aux(point.x, box.left, box.right)
        local dy = distance_aux(point.y, box.top, box.bottom)
        if (inside(point, box))
            return min(dx, dy)    // or 0 in case of distance from the area
        else
            return sqrt(dx * dx + dy * dy)
        endif
    end
    
    0 讨论(0)
  • 2020-12-13 06:55

    This is easy to achieve with dot products. In fact it has been answered in 3d already for the non axis aligned case.

    https://stackoverflow.com/a/44824522/158285

    But in 2D you can achieve the same

    public struct Vector2 {
       double X; double Y
    
       // Vector dot product
       double Dot(Vector2 other)=>X*other.X+Y*other.Y;
    
    
       // Length squared of the vector
       double LengthSquared()=>Dot(this,this);
    
       // Plus other methods for multiplying by a scalar
       // adding and subtracting vectors etc
    }
    

    The function to return the closest point

    public Vector2 ClosestPointTo
        (Vector2 q, Vector2 origin, Vector3 v10, Vector3 v01)
    {
        var px = v10;
        var py = v01;
    
    
        var vx = (px - origin);
        var vy = (py - origin);
    
    
        var tx = Vector2.Dot( q - origin, vx ) / vx.LengthSquared();
        var ty = Vector3.Dot( q - origin, vy ) / vy.LengthSquared();
    
    
        tx = tx < 0 ? 0 : tx > 1 ? 1 : tx;
        ty = ty < 0 ? 0 : ty > 1 ? 1 : ty;
    
        var p = tx * vx + ty * vy + origin;
    
        return p;
    }
    
    0 讨论(0)
  • 2020-12-13 06:58

    Lightly optimized C# alternative (although there should probably be some tolerance when comparing doubles against 0). I also recommend creating some kind of Rect or Point extension methods for these.

    public static class GeometryUtils
    {
        public static double Distance(Point point, Rect rect)
        {
            var xDist = MinXDistance(point, rect);
            var yDist = MinYDistance(point, rect);
            if (xDist == 0)
            {
                return yDist;
            }
            else if (yDist == 0)
            {
                return xDist;
            }
    
            return Math.Sqrt(Math.Pow(xDist, 2) + Math.Pow(yDist, 2));
        }
    
        private static double MinXDistance(Point point, Rect rect)
        {
            if (rect.Left > point.X)
            {
                return rect.Left - point.X;
            }
            else if (rect.Right < point.X)
            {
                return point.X - rect.Right;
            }
            else
            {
                return 0;
            }
        }
    
        private static double MinYDistance(Point point, Rect rect)
        {
            if (rect.Bottom < point.Y)
            {
                return point.Y - rect.Bottom;
            }
            else if (rect.Top > point.Y)
            {
                return rect.Top - point.Y;
            }
            else
            {
                return 0;
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-13 06:59

    Is this a 3D box or a 2D rectangle? Either way you're probably best off getting the point-line (for 2D) or point-plane (3D) distance for each side, then selecting the minimum.

    Edit: there's a much better way described here (last post). It involves transforming your point coordinates into box space, then "saturating" the coordinates with the box size to find the point on the box closest to the point. I haven't tried it, but it looks right to me.

    0 讨论(0)
  • 2020-12-13 07:03

    Here is a single formula that avoids all the case logic. (I happen to be working in JS right now, so here's a JS implementation). Let rect = {max:{x:_, y:_}, min:{x:_, y:_}} and p={x:_, y:_}

    function distance(rect, p) {
      var dx = Math.max(rect.min.x - p.x, 0, p.x - rect.max.x);
      var dy = Math.max(rect.min.y - p.y, 0, p.y - rect.max.y);
      return Math.sqrt(dx*dx + dy*dy);
    }
    

    Explanation: This breaks down the problem into calculating the x distance dx and the y distance dy. It then uses distance formula.

    For calculating dx, here is how that works. (dy is analogous)

    Look at the tuple being provided to the max function: (min-p, 0, p-max). Let's designate this tuple (a,b,c).

    If p is left of min, then we have p < min < max, which means the tuple will evaluate to (+,0,-), and so the max function will correctly return a = min - p.

    If p is between min and max, then we have min < p < max, which means the tuple will evaluate to (-,0,-). So again, the max function will correctly return b = 0.

    Lastly, if p is to the right of max, then we have, min < max < p, and the tuple evaluates to (-,0,+). Once again, Math.max correctly returns c = p - max.

    So it turns out all the case logic is taken care of by Math.max, which leads to a nice 3-line, control-flow-free function.

    0 讨论(0)
  • 2020-12-13 07:03

    Let's say that the point is named P and ABCD is our rectangle. Then the problem can be decomposed into the following set of subproblems:

    (1) Develop a function dist(P, AB) that calculates the distance between a point P and an arbitrary segment AB.

    (2) Calculate four distances between your point P and each side of the rectangle (each side is a segment) and take the shortest of the four

      distance = min(dist(P, AB), dist(P,BC), dist(P, CD), dist(P, DA))
    

    That's your answer.

    Now, we need to know how to calculate the distance between point P and an arbitrary segment AB, i.e. how to calculate dist(P, AB). This is done as follows

    (1) Perform a perpendicular projection of the point P to the line AB. You get the new point P' on AB.

    (2) If P' lies between A and B, then dist(P, AB) is the distance between P and P'.

    (3) Otherwise, dist(P, AB) is the distance between P and either A or B, whichever is shorter.

    That's it. There are some obvious ways to optimize the procedure, but even if implemented literally, it will work very well already.

    P.S. Of course, one can ask how to perform a projection of a point to a line. I'll leave it as an exercise to the reader :)

    0 讨论(0)
提交回复
热议问题