簡體   English   中英

如何計算兩個矩形之間的距離? (上下文:Lua 中的游戲。)

[英]How to calculate distance between two rectangles? (Context: a game in Lua.)

給定兩個具有 x、y、寬度、高度(以像素為單位)和旋轉值(以度為單位)的矩形——如何計算它們的輪廓彼此之間的最近距離?

背景:在 Lua 中編寫的游戲中,我隨機生成地圖,但想確保某些矩形不會彼此太靠近——這是必需的,因為如果矩形進入某個近距離 position,地圖將變得無法解決,因為球需要在他們之間傳遞。 速度不是一個大問題,因為我沒有很多矩形,而且每個級別只生成一次 map。 我在 StackOverflow 上找到的以前的鏈接是這個這個

提前謝謝了!

不在 Lua 中,這是基於 M Katz 建議的 Python 代碼:

def rect_distance((x1, y1, x1b, y1b), (x2, y2, x2b, y2b)):
    left = x2b < x1
    right = x1b < x2
    bottom = y2b < y1
    top = y1b < y2
    if top and left:
        return dist((x1, y1b), (x2b, y2))
    elif left and bottom:
        return dist((x1, y1), (x2b, y2b))
    elif bottom and right:
        return dist((x1b, y1), (x2, y2b))
    elif right and top:
        return dist((x1b, y1b), (x2, y2))
    elif left:
        return x1 - x2b
    elif right:
        return x2 - x1b
    elif bottom:
        return y1 - y2b
    elif top:
        return y2 - y1b
    else:             # rectangles intersect
        return 0.

哪里

  • dist是點之間的歐幾里德距離
  • 矩形 1 由點(x1, y1)(x1b, y1b)
  • 矩形 2 由點(x2, y2)(x2b, y2b)

編輯:正如 OK 指出的那樣,此解決方案假定所有矩形都是直立的。 為了使其適用於 OP 要求的旋轉矩形,您還必須計算從每個矩形的角到另一個矩形最近邊的距離。 但是,如果該點位於線段的兩個端點的上方或下方,並且位於兩條線段的左側或右側(在電話位置 1、3、7 或 9 中,相對於線段)。

Agnius 的答案依賴於 DistanceBetweenLineSegments() 函數。 這是一個沒有的案例分析:

(1) Check if the rects intersect. If so, the distance between them is 0.
(2) If not, think of r2 as the center of a telephone key pad, #5.
(3) r1 may be fully in one of the extreme quadrants (#1, #3, #7, or #9). If so
    the distance is the distance from one rect corner to another (e.g., if r1 is
    in quadrant #1, the distance is the distance from the lower-right corner of
    r1 to the upper-left corner of r2).
(4) Otherwise r1 is to the left, right, above, or below r2 and the distance is
    the distance between the relevant sides (e.g., if r1 is above, the distance
    is the distance between r1's low y and r2's high y).

實際上有一個快速的數學解決方案。

Length(Max((0, 0), Abs(Center - otherCenter) - (Extent + otherExtent)))

其中Center = ((Maximum - Minimum) / 2) + Minimum and Extent = (Maximum - Minimum) / 2 基本上零軸上方的代碼是重疊的,因此距離總是正確的。

最好將矩形保持在這種格式,因為它在許多情況下更可取(ae 旋轉更容易)。

偽代碼:

distance_between_rectangles = some_scary_big_number;
對於 Rectangle1 中的每個 edge1:
對於 Rectangle2 中的每個 edge2:
距離 = 計算edge1 和 edge2 之間的最短距離
如果(距離 < distance_between_rectangles)
distance_between_rectangles = 距離

有很多算法可以解決這個問題,Agnius 算法工作正常。 但是我更喜歡下面的,因為它看起來更直觀(你可以在一張紙上做),而且它們不依賴於找到線之間的最小距離,而是點和線之間的距離。

困難的部分是實現數學函數來找到一條線和一個點之間的距離,以及確定一個點是否面對一條線。 不過,您可以使用簡單的三角函數來解決所有這些問題。 我有以下方法來做到這一點。

對於任意角度的多邊形(三角形、矩形、六邊形等)

  1. 如果多邊形重疊,則返回 0
  2. 在兩個多邊形的中心之間畫一條線。
  3. 從每個多邊形中選擇相交邊。 (這里我們減少問題)
  4. 找出距這兩條邊的最小距離。 (您可以循環遍歷每 4 個點並尋找到另一個形狀邊緣的最小距離)。

只要形狀的任意兩條邊不會產生超過 180 度的角度,這些算法就可以工作。 原因是,如果某物超過 180 度,則意味着某些角落在內部膨脹,就像星星一樣。

邊與點之間的最小距離

  1. 如果點不面向面,則返回點和邊角之間的兩個距離中的最小者。
  2. 從三個點(邊緣的點加上單獨的點)繪制一個三角形。
  3. 我們可以很容易地用勾股定理得到三條繪制線之間的距離。
  4. 海倫公式求三角形的面積。
  5. 現在用Area = 12⋅base⋅height計算高度, base是邊的長度。

檢查點是否面向邊緣

和以前一樣,你用一條邊和一個點制作一個三角形。 現在使用余弦定律,您只需知道邊緣距離就可以找到所有角度。 只要邊到點的每個角度都在 90 度以下,則該點面向邊。

如果您有興趣,我在這里有一個 Python 實現。

請檢查 Java,它具有所有矩形平行的約束,對於所有相交矩形,它返回 0:

   public static double findClosest(Rectangle rec1, Rectangle rec2) {
      double x1, x2, y1, y2;
      double w, h;
      if (rec1.x > rec2.x) {
         x1 = rec2.x; w = rec2.width; x2 = rec1.x;
      } else {
         x1 = rec1.x; w = rec1.width; x2 = rec2.x;
      }
      if (rec1.y > rec2.y) {
         y1 = rec2.y; h = rec2.height; y2 = rec1.y;
      } else {
         y1 = rec1.y; h = rec1.height; y2 = rec2.y;
      }
      double a = Math.max(0, x2 - x1 - w);
      double b = Math.max(0, y2 - y1 - h);
      return Math.sqrt(a*a+b*b);
   }

這個問題要看什么樣的距離。 您想要中心距離、邊緣距離還是最近角的距離?

我猜你是說最后一個。 如果 X 和 Y 值指示矩形的中心,那么您可以通過應用此技巧找到每個角

//Pseudo code
Vector2 BottomLeftCorner = new Vector2(width / 2, heigth / 2);
BottomLeftCorner = BottomLeftCorner * Matrix.CreateRotation(MathHelper.ToRadians(degrees));
//If LUA has no built in Vector/Matrix calculus search for "rotate Vector" on the web.
//this helps: http://www.kirupa.com/forum/archive/index.php/t-12181.html

BottomLeftCorner += new Vector2(X, Y); //add the origin so that we have to world position.

對所有矩形的所有角執行此操作,然后循環遍歷所有角並計算距離(僅 abs(v1 - v2))。

我希望這對你有幫助

我只是在 n 維中為此編寫了代碼。 我無法輕松找到通用解決方案。

// considering a rectangle object that contains two points (min and max)
double distance(const rectangle& a, const rectangle& b) const {
    // whatever type you are using for points
    point_type closest_point;
    for (size_t i = 0; i < b.dimensions(); ++i) {
        closest_point[i] = b.min[i] > a.min[i] ? a.max[i] : a.min[i];
    }
    // use usual euclidian distance here
    return distance(a, closest_point);
}

要計算矩形和點之間的距離,您可以:

double distance(const rectangle& a, const point_type& p) const {
    double dist = 0.0;
    for (size_t i = 0; i < dimensions(); ++i) {
        double di = std::max(std::max(a.min[i] - p[i], p[i] - a.max[i]), 0.0);
        dist += di * di;
    }
    return sqrt(dist);
}

如果要旋轉其中一個矩形,則需要旋轉坐標系。

如果要旋轉兩個矩形,可以旋轉矩形a的坐標系。 然后我們必須改變這一行:

closest_point[i] = b.min[i] > a.min[i] ? a.max[i] : a.min[i];

因為這認為b只有一個候選者是最近的頂點。 您必須更改它以檢查到b所有頂點的距離。 它始終是頂點之一。

請參閱: https : //i.stack.imgur.com/EKJmr.png

我解決問題的方法:

  1. 將兩個矩形合並為一個大矩形
  2. 從大矩形中減去第一個矩形和第二個矩形
  3. 相減后剩下的就是兩個矩形之間的一個矩形,這個矩形的對角線就是兩個矩形之間的距離。

這是 C# 中的示例

public static double GetRectDistance(this System.Drawing.Rectangle rect1, System.Drawing.Rectangle rect2)
{
    if (rect1.IntersectsWith(rect2))
    {
        return 0;
    }

    var rectUnion = System.Drawing.Rectangle.Union(rect1, rect2);
    rectUnion.Width -= rect1.Width + rect2.Width;
    rectUnion.Width = Math.Max(0, rectUnion.Width);

    rectUnion.Height -= rect1.Height + rect2.Height;
    rectUnion.Height = Math.Max(0, rectUnion.Height);

    return rectUnion.Diagonal();
}

public static double Diagonal(this System.Drawing.Rectangle rect)
{
    return Math.Sqrt(rect.Height * rect.Height + rect.Width * rect.Width);
}

另一種解決方案,它計算矩形上的點數並選擇距離最小的一對。

優點:適用於所有多邊形。

缺點:准確性稍差且速度較慢。

import numpy as np
import math

POINTS_PER_LINE = 100

# get points on polygon outer lines
# format of polygons: ((x1, y1), (x2, y2), ...)
def get_points_on_polygon(poly, points_per_line=POINTS_PER_LINE):

    all_res = []

    for i in range(len(poly)):

        a = poly[i]

        if i == 0:
            b = poly[-1]

        else:
            b = poly[i-1]

        res = list(np.linspace(a, b, points_per_line))

        all_res += res

    return all_res



# compute minimum distance between two polygons
# format of polygons: ((x1, y1), (x2, y2), ...)
def min_poly_distance(poly1, poly2, points_per_line=POINTS_PER_LINE):

    poly1_points = get_points_on_polygon(poly1, points_per_line=points_per_line)

    poly2_points = get_points_on_polygon(poly2, points_per_line=points_per_line)

    distance = min([math.sqrt((a[0] - b[0])**2 + (a[1] - b[1])**2) for a in poly1_points for b in poly2_points])

    # slower
    # distance = min([np.linalg.norm(a - b) for a in poly1_points for b in poly2_points])

    return distance

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM